diff --git a/src/main/dig/combinatorial/Demultiplexer.dig b/src/main/dig/combinatorial/Demultiplexer.dig
index 61d1f389d..14b8e3deb 100644
--- a/src/main/dig/combinatorial/Demultiplexer.dig
+++ b/src/main/dig/combinatorial/Demultiplexer.dig
@@ -1,311 +1,311 @@
- 1
-
-
- In
-
-
- Label
- A_0
-
-
-
- 0
-
-
- In
-
-
- Label
- A_1
-
-
-
- 0
-
-
- Not
-
-
- rotation
-
-
-
-
- 3
-
-
- Not
-
-
- rotation
-
-
-
-
- 3
-
-
- And
-
-
- Inputs
- 3
-
-
-
- 0
-
-
- In
-
-
- Label
- D
-
-
-
- 0
-
-
- And
-
-
- Inputs
- 3
-
-
-
- 0
-
-
- And
-
-
- Inputs
- 3
-
-
-
- 0
-
-
- And
-
-
- Inputs
- 3
-
-
-
- 0
-
-
- Out
-
-
- Label
- Y_0
-
-
-
- 0
-
-
- Out
-
-
- Label
- Y_1
-
-
-
- 0
-
-
- Out
-
-
- Label
- Y_2
-
-
-
- 0
-
-
- Out
-
-
- Label
- Y_3
-
-
-
- 0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ 1
+
+
+ In
+
+
+ Label
+ A_1
+
+
+
+ 0
+
+
+ In
+
+
+ Label
+ A_0
+
+
+
+ 0
+
+
+ Not
+
+
+ rotation
+
+
+
+
+ 3
+
+
+ Not
+
+
+ rotation
+
+
+
+
+ 3
+
+
+ And
+
+
+ Inputs
+ 3
+
+
+
+ 0
+
+
+ In
+
+
+ Label
+ D
+
+
+
+ 0
+
+
+ And
+
+
+ Inputs
+ 3
+
+
+
+ 0
+
+
+ And
+
+
+ Inputs
+ 3
+
+
+
+ 0
+
+
+ And
+
+
+ Inputs
+ 3
+
+
+
+ 0
+
+
+ Out
+
+
+ Label
+ Y_0
+
+
+
+ 0
+
+
+ Out
+
+
+ Label
+ Y_1
+
+
+
+ 0
+
+
+ Out
+
+
+ Label
+ Y_2
+
+
+
+ 0
+
+
+ Out
+
+
+ Label
+ Y_3
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/dig/combinatorial/FullAdder.dig b/src/main/dig/combinatorial/FullAdder.dig
index 3d249f1d4..800ca3254 100644
--- a/src/main/dig/combinatorial/FullAdder.dig
+++ b/src/main/dig/combinatorial/FullAdder.dig
@@ -76,10 +76,10 @@
Label
- C_i
+ S_i
-
+
0
@@ -87,10 +87,10 @@
Label
- S_i
+ C_i
-
+
0
diff --git a/src/main/java/de/neemann/digital/analyse/AnalyseException.java b/src/main/java/de/neemann/digital/analyse/AnalyseException.java
new file mode 100644
index 000000000..4970ca7ca
--- /dev/null
+++ b/src/main/java/de/neemann/digital/analyse/AnalyseException.java
@@ -0,0 +1,17 @@
+package de.neemann.digital.analyse;
+
+/**
+ * Exception thrown if there problems analysing the circuit
+ *
+ * @author hneemann
+ */
+public class AnalyseException extends Exception {
+ /**
+ * Creates a new instance
+ *
+ * @param message the message
+ */
+ public AnalyseException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/de/neemann/digital/analyse/ModelAnalyser.java b/src/main/java/de/neemann/digital/analyse/ModelAnalyser.java
new file mode 100644
index 000000000..54e4c5853
--- /dev/null
+++ b/src/main/java/de/neemann/digital/analyse/ModelAnalyser.java
@@ -0,0 +1,138 @@
+package de.neemann.digital.analyse;
+
+import de.neemann.digital.core.Model;
+import de.neemann.digital.core.NodeException;
+import de.neemann.digital.lang.Lang;
+
+import java.util.ArrayList;
+
+/**
+ * Analyses a given model.
+ * Calculates the truth table which generated by the given model
+ *
+ * @author hneemann
+ */
+public class ModelAnalyser {
+
+ private final Model model;
+ private final ArrayList inputs;
+ private final ArrayList outputs;
+ private final int rows;
+ private final int cols;
+ private final boolean[][] data;
+
+
+ /**
+ * Creates a new instance
+ *
+ * @param model the model
+ * @throws AnalyseException AnalyseException
+ */
+ public ModelAnalyser(Model model) throws AnalyseException {
+ this.model = model;
+ inputs = checkBinary(model.getInputs());
+ if (inputs.size() == 0)
+ throw new AnalyseException(Lang.get("err_analyseNoInputs"));
+ outputs = checkBinary(model.getOutputs());
+ if (outputs.size() == 0)
+ throw new AnalyseException(Lang.get("err_analyseNoOutputs"));
+ rows = 1 << inputs.size();
+ cols = inputs.size() + outputs.size();
+ data = new boolean[rows][cols];
+ }
+
+ private ArrayList checkBinary(ArrayList list) throws AnalyseException {
+ for (Model.Signal s : list)
+ if (s.getValue().getBits() != 1)
+ throw new AnalyseException(Lang.get("err_analyseValue_N_IsNotBinary", s.getName()));
+ return list;
+ }
+
+ /**
+ * Analyses the circuit
+ *
+ * @return this for call chaining
+ * @throws NodeException NodeException
+ */
+ public ModelAnalyser analyse() throws NodeException {
+ model.init();
+ for (int row = 0; row < rows; row++) {
+ for (int i = 0; i < inputs.size(); i++) {
+ int pos = inputs.size() - 1 - i;
+ boolean bool = (row & (1 << pos)) != 0;
+ inputs.get(i).getValue().setBool(bool);
+ data[row][i] = bool;
+ }
+ model.doStep();
+ for (int i = 0; i < outputs.size(); i++) {
+ data[row][inputs.size() + i] = outputs.get(i).getValue().getBool();
+ }
+ }
+ return this;
+ }
+
+ /**
+ * @return returns the models inputs
+ */
+ public ArrayList getInputs() {
+ return inputs;
+ }
+
+ /**
+ * @return returns the models outputs
+ */
+ public ArrayList getOutputs() {
+ return outputs;
+ }
+
+ /**
+ * @return the truth table
+ */
+ public boolean[][] getData() {
+ return data;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (Model.Signal s : inputs)
+ sb.append(s.getName()).append("\t");
+ for (Model.Signal s : outputs)
+ sb.append(s.getName()).append("\t");
+ sb.append('\n');
+ for (int row = 0; row < rows; row++) {
+ for (int col = 0; col < cols; col++) {
+ if (data[row][col])
+ sb.append("1\t");
+ else
+ sb.append("0\t");
+ }
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * @return the number of rows
+ */
+ public int getRows() {
+ return rows;
+ }
+
+ /**
+ * @return the number of columns
+ */
+ public int getCols() {
+ return cols;
+ }
+
+ /**
+ * @return returns one value of the truth table
+ */
+ public int getValue(int rowIndex, int columnIndex) {
+ if (data[rowIndex][columnIndex])
+ return 1;
+ else
+ return 0;
+ }
+}
diff --git a/src/main/java/de/neemann/digital/analyse/ModelAnalyzerTableModel.java b/src/main/java/de/neemann/digital/analyse/ModelAnalyzerTableModel.java
new file mode 100644
index 000000000..536083cc1
--- /dev/null
+++ b/src/main/java/de/neemann/digital/analyse/ModelAnalyzerTableModel.java
@@ -0,0 +1,60 @@
+package de.neemann.digital.analyse;
+
+import javax.swing.event.TableModelListener;
+import javax.swing.table.TableModel;
+
+/**
+ * @author hneemann
+ */
+public class ModelAnalyzerTableModel implements TableModel {
+ private final ModelAnalyser analyzer;
+
+ public ModelAnalyzerTableModel(ModelAnalyser analyzer) {
+ this.analyzer = analyzer;
+ }
+
+ @Override
+ public int getRowCount() {
+ return analyzer.getRows();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return analyzer.getCols();
+ }
+
+ @Override
+ public String getColumnName(int columnIndex) {
+ if (columnIndex < analyzer.getInputs().size())
+ return analyzer.getInputs().get(columnIndex).getName();
+ else
+ return analyzer.getOutputs().get(columnIndex - analyzer.getInputs().size()).getName();
+ }
+
+ @Override
+ public Class> getColumnClass(int columnIndex) {
+ return Integer.class;
+ }
+
+ @Override
+ public boolean isCellEditable(int rowIndex, int columnIndex) {
+ return false;
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ return analyzer.getValue(rowIndex, columnIndex);
+ }
+
+ @Override
+ public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+ }
+
+ @Override
+ public void addTableModelListener(TableModelListener l) {
+ }
+
+ @Override
+ public void removeTableModelListener(TableModelListener l) {
+ }
+}
diff --git a/src/main/java/de/neemann/digital/analyse/package-info.java b/src/main/java/de/neemann/digital/analyse/package-info.java
new file mode 100644
index 000000000..e00b063c5
--- /dev/null
+++ b/src/main/java/de/neemann/digital/analyse/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * Classes used to analyse the actual circuit
+ *
+ * @author hneemann
+ */
+package de.neemann.digital.analyse;
diff --git a/src/main/java/de/neemann/digital/core/Model.java b/src/main/java/de/neemann/digital/core/Model.java
index 747724020..7946ee691 100644
--- a/src/main/java/de/neemann/digital/core/Model.java
+++ b/src/main/java/de/neemann/digital/core/Model.java
@@ -48,6 +48,8 @@ public class Model {
private final ArrayList breaks;
private final ArrayList resets;
private final ArrayList signals;
+ private final ArrayList inputs;
+ private final ArrayList outputs;
private final ArrayList roms;
private final ArrayList nodes;
@@ -65,6 +67,8 @@ public class Model {
this.breaks = new ArrayList<>();
this.resets = new ArrayList<>();
this.signals = new ArrayList<>();
+ this.outputs = new ArrayList<>();
+ this.inputs = new ArrayList<>();
this.roms = new ArrayList<>();
this.nodes = new ArrayList<>();
this.nodesToUpdateAct = new ArrayList<>();
@@ -100,8 +104,7 @@ public class Model {
/**
* Needs to be called after all nodes are added.
- * If not called it es called automatically.
- * Calles init(true);
+ * Calls init(true);
*
* @throws NodeException NodeException
*/
@@ -373,10 +376,56 @@ public class Model {
* @param value the signals value
*/
public void addSignal(String name, ObservableValue value) {
- if (name != null && name.length() > 0 && value != null)
+ if (isSignal(name, value))
signals.add(new Signal(name, value));
}
+ private static boolean isSignal(String name, ObservableValue value) {
+ return name != null && name.length() > 0 && value != null;
+ }
+
+ /**
+ * registers a input to the model
+ *
+ * @param name the signals name
+ * @param value the signals value
+ */
+ public void addInput(String name, ObservableValue value) {
+ if (isSignal(name, value)) {
+ Signal signal = new Signal(name, value);
+ signals.add(signal);
+ inputs.add(signal);
+ }
+ }
+
+ /**
+ * @return the models inputs
+ */
+ public ArrayList getInputs() {
+ return inputs;
+ }
+
+ /**
+ * registers a output to the model
+ *
+ * @param name the signals name
+ * @param value the signals value
+ */
+ public void addOutput(String name, ObservableValue value) {
+ if (isSignal(name, value)) {
+ Signal signal = new Signal(name, value);
+ signals.add(signal);
+ outputs.add(signal);
+ }
+ }
+
+ /**
+ * @return the models outputs
+ */
+ public ArrayList getOutputs() {
+ return outputs;
+ }
+
/**
* @return all registered Signals
*/
@@ -431,7 +480,10 @@ public class Model {
* @param value the signals value
*/
public Signal(String name, ObservableValue value) {
- this.name = name;
+ if (name.length() > 2 && name.charAt(0) == '$' && name.charAt(name.length() - 1) == '$')
+ this.name = name.substring(1, name.length() - 1);
+ else
+ this.name = name;
this.value = value;
}
diff --git a/src/main/java/de/neemann/digital/core/io/In.java b/src/main/java/de/neemann/digital/core/io/In.java
index 40e0cc52d..14de670eb 100644
--- a/src/main/java/de/neemann/digital/core/io/In.java
+++ b/src/main/java/de/neemann/digital/core/io/In.java
@@ -59,6 +59,6 @@ public class In implements Element {
@Override
public void registerNodes(Model model) {
- model.addSignal(label, output);
+ model.addInput(label, output);
}
}
diff --git a/src/main/java/de/neemann/digital/core/io/Out.java b/src/main/java/de/neemann/digital/core/io/Out.java
index 3309d99f0..a2ca82613 100644
--- a/src/main/java/de/neemann/digital/core/io/Out.java
+++ b/src/main/java/de/neemann/digital/core/io/Out.java
@@ -104,6 +104,6 @@ public class Out implements Element {
@Override
public void registerNodes(Model model) {
- model.addSignal(label, value);
+ model.addOutput(label, value);
}
}
diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java
index acc8c3e91..fa947980d 100644
--- a/src/main/java/de/neemann/digital/gui/Main.java
+++ b/src/main/java/de/neemann/digital/gui/Main.java
@@ -1,5 +1,7 @@
package de.neemann.digital.gui;
+import de.neemann.digital.analyse.AnalyseException;
+import de.neemann.digital.analyse.ModelAnalyser;
import de.neemann.digital.core.*;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.Key;
@@ -320,6 +322,8 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
run.add(speedTest.createJMenuItem());
doStep.setEnabled(false);
+ bar.add(createAanalyseMenu());
+
JToolBar toolBar = new JToolBar();
toolBar.add(newFile.createJButtonNoText());
toolBar.add(open.createJButtonNoText());
@@ -348,6 +352,26 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
setLocationRelativeTo(parent);
}
+ private JMenu createAanalyseMenu() {
+ JMenu analyse = new JMenu(Lang.get("menu_analyse"));
+ analyse.add(new ToolTipAction(Lang.get("menu_analyse")) {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ try {
+ Model model = new ModelDescription(circuitComponent.getCircuit(), library).createModel();
+ new TableDialog(Main.this, new ModelAnalyser(model).analyse()).setVisible(true);
+ elementState.activate();
+ } catch (PinException | NodeException | AnalyseException e1) {
+ showErrorAndStopModel(Lang.get("msg_annalyseErr"), e1);
+ }
+ }
+ }
+ .setToolTip(Lang.get("menu_analyse_tt"))
+ .createJMenuItem());
+
+ return analyse;
+ }
+
private void orderMeasurements() {
try {
Model m = new ModelDescription(circuitComponent.getCircuit(), library).createModel();
diff --git a/src/main/java/de/neemann/digital/gui/components/TableDialog.java b/src/main/java/de/neemann/digital/gui/components/TableDialog.java
new file mode 100644
index 000000000..449ac6d84
--- /dev/null
+++ b/src/main/java/de/neemann/digital/gui/components/TableDialog.java
@@ -0,0 +1,30 @@
+package de.neemann.digital.gui.components;
+
+import de.neemann.digital.analyse.ModelAnalyser;
+import de.neemann.digital.analyse.ModelAnalyzerTableModel;
+import de.neemann.digital.lang.Lang;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * Dialog to visualise a truth table.
+ *
+ * @author hneemann
+ */
+public class TableDialog extends JDialog {
+ /**
+ * Creates a new instance
+ *
+ * @param owner the owner of this dialog
+ * @param analyzer the truth table
+ */
+ public TableDialog(Frame owner, ModelAnalyser analyzer) {
+ super(owner, Lang.get("win_table"));
+ JTable table = new JTable(new ModelAnalyzerTableModel(analyzer));
+ table.setPreferredScrollableViewportSize(table.getPreferredSize());
+ getContentPane().add(new JScrollPane(table));
+ pack();
+ setLocationRelativeTo(owner);
+ }
+}
diff --git a/src/main/resources/lang/lang_de.properties b/src/main/resources/lang/lang_de.properties
index 7ed92a9ba..e6e4e10f4 100644
--- a/src/main/resources/lang/lang_de.properties
+++ b/src/main/resources/lang/lang_de.properties
@@ -168,6 +168,9 @@ err_notAllOutputsSameBits=Es haben nicht alle Ausg\u00E4nge die gleiche Bitbreit
err_notAllOutputsSupportHighZ=Wenn mehrere Ausg\u00E4nge verbunden sind, m\u00FCssen alle Ausg\u00E4nge Tri-State Ausg\u00E4nge sein
err_breakTimeOut=Nach {0} Zyklen ist kein Break aufgetreten
err_labelNotConnectedToNet_N=Ein Tunnel {0} ist nicht verbunden!
+err_analyseNoInputs=Die Schaltung hat keine benannten Eing\u00E4nge
+err_analyseNoOutputs=Die Schaltung hat keine benannten Ausg\u00E4nge
+err_analyseValue_N_IsNotBinary=Der Wert {0} hat mehr als ein Bit.
attr_dialogTitle=Eigenschaften
msg_errorEditingValue=Fehler bei der Eingabe eines Wertes
@@ -178,10 +181,11 @@ msg_errorWritingFile=Fehler beim Schreiben einer Datei
msg_errorReadingFile=Fehler beim Lesen einer Datei
msg_errorCalculatingStep=Fehler beim Berechnen eines Schrittes
msg_missingShape_N=Es fehlt ein Diagramm f\u00FCr {0}
-msg_fastRunError=Ein Fehler beim Schnellen Lauf
+msg_fastRunError=Ein Fehler beim schnellen Lauf
msg_errorReadingListing_N0=Fehler beim Laden des Listings {0}
msg_clockError=Fehler bei der Berechnung einer Takt\u00E4nderung
msg_frequency_N=Die maximale Frequenz ber\u00E4gt {0}Hz.
+msg_annalyseErr=Fehler bei der Analyse der Schaltung
stat_clocks={0} Halbzyklen
@@ -244,6 +248,8 @@ menu_editRunAttributes=Simulationseinstellungen
menu_editRunAttributes_tt=Einstellungen f\u00FCr den Start der Simulation
menu_saveData=Daten speichern
menu_saveData_tt=Speichert die Daten als CSV Datei
+menu_analyse=Analyse
+menu_analyse_tt=Analyse der aktuellen Schaltung
menu_about=\u00DCber Digital
@@ -255,3 +261,4 @@ win_measures_microstep=Messwerte im Einzelgattermodus
win_measures_fullstep=Messwerte im Vollschrittmodus
win_listing=Listing
+win_table=Tabelle
\ No newline at end of file
diff --git a/src/main/resources/lang/lang_en.properties b/src/main/resources/lang/lang_en.properties
index 4cbb5cf37..b605fd2c9 100644
--- a/src/main/resources/lang/lang_en.properties
+++ b/src/main/resources/lang/lang_en.properties
@@ -228,6 +228,8 @@ menu_editRunAttributes=Simulation Settings
menu_editRunAttributes_tt=Settings used to start the simulation
menu_saveData=Save Data
menu_saveData_tt=Save data as CSV file
+menu_analyse=Analyse
+menu_analyse_tt=Analyses the actual circuit
win_saveChanges=Save Changes?
win_confirmExit=Confirm Exit!
@@ -252,3 +254,8 @@ elem_Text_tt=Shows a text in the circuit
key_Default_tt=Is set if the model is started
err_labelNotConnectedToNet_N=A tunnel {0} is not connected!
key_Width_tt=With of symbol if this circuit is used in an element ins an other circuit.
+err_analyseNoInputs=The circuit has no Inputs
+err_analyseNoOutputs=The circuit has no outputs
+err_analyseValue_N_IsNotBinary=The value {0} has more the one bit.
+msg_annalyseErr=Error analysing the circuit
+win_table=Table
diff --git a/src/test/java/de/neemann/digital/analyse/ModelAnalyserTest.java b/src/test/java/de/neemann/digital/analyse/ModelAnalyserTest.java
new file mode 100644
index 000000000..5952f4694
--- /dev/null
+++ b/src/test/java/de/neemann/digital/analyse/ModelAnalyserTest.java
@@ -0,0 +1,31 @@
+package de.neemann.digital.analyse;
+
+import de.neemann.digital.core.Model;
+import de.neemann.digital.integration.ToBreakRunner;
+import junit.framework.TestCase;
+
+/**
+ * @author hneemann
+ */
+public class ModelAnalyserTest extends TestCase {
+
+ public void testAnalyzer() throws Exception {
+ Model model = new ToBreakRunner("dig/analyzeTest.dig").getModel();
+ ModelAnalyser ma = new ModelAnalyser(model).analyse();
+
+ assertEquals(4, ma.getRows());
+ assertEquals(3, ma.getCols());
+
+ // circuit is XOr:
+ assertEquals(0, ma.getValue(0, 2));
+ assertEquals(1, ma.getValue(1, 2));
+ assertEquals(1, ma.getValue(2, 2));
+ assertEquals(0, ma.getValue(3, 2));
+
+ assertEquals("A\tB\tY\t\n" +
+ "0\t0\t0\t\n" +
+ "0\t1\t1\t\n" +
+ "1\t0\t1\t\n" +
+ "1\t1\t0\t\n", ma.toString());
+ }
+}
\ No newline at end of file