diff --git a/distribution/ReleaseNotes.txt b/distribution/ReleaseNotes.txt
index b937638ba..453093fdd 100644
--- a/distribution/ReleaseNotes.txt
+++ b/distribution/ReleaseNotes.txt
@@ -3,8 +3,9 @@ Release Notes
HEAD, planned as v0.19
- Added support for asynchronous sequential circuits such as the Muller-pipeline.
Take a look at the new asynchronous examples for illustration.
-- Braking changes:
+- Breaking changes:
- Added an enable input to the terminal component.
+ - Added a clock input to the keyboard component.
- Bug fixes
- Fixed a bug in the VHDL export concerning an invalid optimization of a
std_logic_vector access.
diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java
index 6304afd38..74d0037d0 100644
--- a/src/main/java/de/neemann/digital/gui/Main.java
+++ b/src/main/java/de/neemann/digital/gui/Main.java
@@ -35,6 +35,8 @@ import de.neemann.digital.gui.components.modification.Modifications;
import de.neemann.digital.gui.components.modification.ModifyAttribute;
import de.neemann.digital.gui.components.modification.ModifyMeasurementOrdering;
import de.neemann.digital.gui.components.table.TableDialog;
+import de.neemann.digital.gui.components.terminal.Keyboard;
+import de.neemann.digital.gui.components.terminal.KeyboardDialog;
import de.neemann.digital.gui.components.testing.ValueTableDialog;
import de.neemann.digital.gui.components.tree.LibraryTreeModel;
import de.neemann.digital.gui.components.tree.SelectTree;
@@ -1221,6 +1223,8 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
// all repainting is initiated by user actions!
modelCreator.connectToGui(null);
+ handleKeyboardComponent(updateEvent, modelSync);
+
doStep.setEnabled(false);
runToBreakAction.setEnabled(!realTimeClockRunning && model.isFastRunModel());
@@ -1250,6 +1254,37 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
return false;
}
+ private void handleKeyboardComponent(ModelEvent updateEvent, Sync modelSync) {
+ KeyboardDialog.KeyPressedHandler handler = null;
+ for (Keyboard k : model.findNode(Keyboard.class)) {
+ if (handler == null)
+ if (updateEvent == ModelEvent.MICROSTEP)
+ handler = keyboard -> {
+ keyboard.hasChanged();
+ modelCreator.addNodeElementsTo(model.nodesToUpdate(), circuitComponent.getHighLighted());
+ model.fireManualChangeEvent();
+ doStep.setEnabled(model.needsUpdate());
+ circuitComponent.repaintNeeded();
+ };
+ else
+ handler = keyboard -> {
+ try {
+ modelSync.accessNEx(() -> {
+ keyboard.hasChanged();
+ model.fireManualChangeEvent();
+ model.doStep();
+ });
+ circuitComponent.repaintNeeded();
+ } catch (NodeException | RuntimeException e) {
+ showErrorAndStopModel(Lang.get("msg_errorCalculatingStep"), e);
+ }
+ };
+
+
+ windowPosManager.register("keyboard_" + k.getLabel(), new KeyboardDialog(this, k, handler));
+ }
+ }
+
private void showMeasurementGraph(ModelEvent updateEvent) {
List ordering = circuitComponent.getCircuit().getMeasurementOrdering();
windowPosManager.register("dataSet", GraphDialog.createLiveDialog(this, model, updateEvent == ModelEvent.MICROSTEP, ordering, modelSync)).setVisible(true);
diff --git a/src/main/java/de/neemann/digital/gui/components/terminal/Keyboard.java b/src/main/java/de/neemann/digital/gui/components/terminal/Keyboard.java
index ae3bd617e..5e033285e 100644
--- a/src/main/java/de/neemann/digital/gui/components/terminal/Keyboard.java
+++ b/src/main/java/de/neemann/digital/gui/components/terminal/Keyboard.java
@@ -14,8 +14,6 @@ import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.ElementTypeDescription;
import de.neemann.digital.core.element.Keys;
-import javax.swing.*;
-
import static de.neemann.digital.core.element.PinInfo.input;
/**
@@ -27,17 +25,20 @@ public class Keyboard extends Node implements Element {
*/
public static final ElementTypeDescription DESCRIPTION
= new ElementTypeDescription(Keyboard.class,
- input("sel"))
+ input("C").setClock(),
+ input("en"))
.addAttribute(Keys.ROTATE)
- .addAttribute(Keys.LABEL);
+ .addAttribute(Keys.LABEL)
+ .addAttribute(Keys.INVERTER_CONFIG);
private final String label;
- private KeyboardDialog keyboardDialog;
- private ObservableValue data;
- private ObservableValue select;
- private boolean sel;
- private boolean lastSel = false;
- private int keyData;
+ private final ObservableValue data;
+ private final ObservableValue isKeyOut;
+ private KeyboardInterface keyboardInterface;
+ private ObservableValue clock;
+ private ObservableValue enable;
+ private boolean enableVal;
+ private boolean lastClock = false;
/**
* Creates a new terminal instance
@@ -48,49 +49,83 @@ public class Keyboard extends Node implements Element {
data = new ObservableValue("D", 16)
.setToHighZ()
.setPinDescription(DESCRIPTION);
+ isKeyOut = new ObservableValue("av", 1)
+ .setPinDescription(DESCRIPTION);
label = attributes.getCleanLabel();
}
@Override
public void setInputs(ObservableValues inputs) throws NodeException {
- select = inputs.get(0).addObserverToValue(this).checkBits(1, this);
+ clock = inputs.get(0).addObserverToValue(this).checkBits(1, this, 0);
+ enable = inputs.get(1).addObserverToValue(this).checkBits(1, this, 1);
}
@Override
public ObservableValues getOutputs() {
- return data.asList();
+ return new ObservableValues(data, isKeyOut);
}
@Override
public void readInputs() throws NodeException {
- sel = select.getBool();
- if (!lastSel && sel) {
- KeyboardDialog kbd = getKeyboard();
- if (kbd != null)
- keyData = kbd.getChar();
- else
- keyData = 0;
- }
- lastSel = sel;
+ enableVal = enable.getBool();
+ boolean nowClock = clock.getBool();
+
+ if (keyboardInterface != null && nowClock && !lastClock && enableVal)
+ keyboardInterface.removeChar();
+
+ lastClock = nowClock;
}
@Override
public void writeOutputs() throws NodeException {
- if (sel)
- data.setValue(keyData);
- else
- data.setToHighZ();
+ if (keyboardInterface != null) {
+ if (enableVal)
+ data.setValue(keyboardInterface.getChar());
+ else
+ data.setToHighZ();
+ isKeyOut.setBool(keyboardInterface.isChar());
+ } else {
+ if (enableVal)
+ data.setValue(0);
+ else
+ data.setToHighZ();
+ isKeyOut.setBool(false);
+ }
}
- private KeyboardDialog getKeyboard() {
- if (keyboardDialog == null || !keyboardDialog.isVisible()) {
- SwingUtilities.invokeLater(() -> {
- if (keyboardDialog == null || !keyboardDialog.isVisible()) {
- keyboardDialog = new KeyboardDialog(getModel().getWindowPosManager().getMainFrame());
- getModel().getWindowPosManager().register("keyboard_" + label, keyboardDialog);
- }
- });
- }
- return keyboardDialog;
+ /**
+ * Sets the keyboard interface
+ *
+ * @param keyboardInterface the keyboard interface
+ */
+ public void setKeyboardDialog(KeyboardInterface keyboardInterface) {
+ this.keyboardInterface = keyboardInterface;
+ }
+
+ /**
+ * @return the keyboard label
+ */
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * The keyboard interface
+ */
+ public interface KeyboardInterface {
+ /**
+ * @return a char or 0 if no char available
+ */
+ int getChar();
+
+ /**
+ * @return true if there is a char
+ */
+ boolean isChar();
+
+ /**
+ * removes the first character
+ */
+ void removeChar();
}
}
diff --git a/src/main/java/de/neemann/digital/gui/components/terminal/KeyboardDialog.java b/src/main/java/de/neemann/digital/gui/components/terminal/KeyboardDialog.java
index 195dbfc25..b75675cee 100644
--- a/src/main/java/de/neemann/digital/gui/components/terminal/KeyboardDialog.java
+++ b/src/main/java/de/neemann/digital/gui/components/terminal/KeyboardDialog.java
@@ -15,7 +15,7 @@ import java.awt.event.KeyEvent;
/**
* A simple keyboard implementation
*/
-public class KeyboardDialog extends JDialog {
+public class KeyboardDialog extends JDialog implements Keyboard.KeyboardInterface {
private final JLabel textLabel;
private final Object textLock = new Object();
private String text;
@@ -23,13 +23,15 @@ public class KeyboardDialog extends JDialog {
/**
* Create a new Instance
*
- * @param owner the owner frame
+ * @param owner the owner frame
+ * @param keyboard the keyboard node which has opened this dialog
+ * @param keyPressedHandler handler called every time a key is typed
*/
- public KeyboardDialog(Frame owner) {
- super(owner, Lang.get("elem_Keyboard"), false);
+ public KeyboardDialog(Frame owner, Keyboard keyboard, KeyPressedHandler keyPressedHandler) {
+ super(owner, Lang.get("elem_Keyboard") + " " + keyboard.getLabel(), false);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
- textLabel = new JLabel("Enter Text ");
+ textLabel = new JLabel(Lang.get("msg_enterText") + " ");
textLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
getContentPane().add(textLabel);
text = "";
@@ -43,6 +45,7 @@ public class KeyboardDialog extends JDialog {
text += e.getKeyChar();
t = text;
}
+ keyPressedHandler.keyPressed(keyboard);
textLabel.setText(t);
}
});
@@ -50,24 +53,47 @@ public class KeyboardDialog extends JDialog {
pack();
setLocationRelativeTo(owner);
setVisible(true);
+
+ keyboard.setKeyboardDialog(this);
}
- /**
- * @return the oldest char
- */
+ @Override
public int getChar() {
synchronized (textLock) {
if (text.length() == 0)
return 0;
- else {
- int c = text.charAt(0);
- String t;
+ else
+ return text.charAt(0);
+ }
+ }
+
+ @Override
+ public boolean isChar() {
+ synchronized (textLock) {
+ return text.length() > 0;
+ }
+ }
+
+ @Override
+ public void removeChar() {
+ synchronized (textLock) {
+ if (text.length() > 0) {
text = text.substring(1);
- t = text;
+ final String t = text;
SwingUtilities.invokeLater(() -> textLabel.setText(t));
- return c;
}
}
}
+ /**
+ * The handler called if a key is typed.
+ */
+ public interface KeyPressedHandler {
+ /**
+ * Called if a key is typed
+ *
+ * @param keyboard the keyboard used
+ */
+ void keyPressed(Keyboard keyboard);
+ }
}
diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml
index 391eadefc..71cb0bc8d 100644
--- a/src/main/resources/lang/lang_de.xml
+++ b/src/main/resources/lang/lang_de.xml
@@ -224,8 +224,13 @@
Dieses Element puffert die Eingaben, welche dann ausgelesen werden können.
Es wird ein eigenes Fenster für die Eingabe geöffnet.
- Wenn gesetzt, ist der Ausgang D aktiv und ein Tastendruck wird ausgegeben.
+ Takt. Eine steigende Flanke entfernt das älteste Zeichen aus dem Buffer.
+ Wenn gesetzt, ist der Ausgang D aktiv und ein Tastendruck wird ausgegeben.
+ Ist gleichzeitig die freigabe für den Takt.
Die letzte Taste oder 0, wenn keine Taste gedrückt.
+ Dieser Ausgang zeigt an, dass Zeichen vorhanden sind.
+ Er kann genutzt werden, um einen Interrupt auszulösen.
+
Terminal
Ein Terminal, auf dem ASCII-Zeichen ausgegeben werden können.
Öffnet ein eigenes Fenster, um die Ausgabe anzuzeigen.
@@ -1429,6 +1434,7 @@ Soll dennoch exportiert werden?
nicht der Fall ist, wird die Fehlermeldung der externen Anwendung angezeigt. Wenn es möglich ist, werden zudem
die Eingangs- und Ausgangsdefinitionen an den aktuellen Code angepasst.
Ausführbare Datei "{0}" nicht gefunden!
+ Text eingeben!
Ok
0°
diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml
index ea4f387f8..092e3440a 100644
--- a/src/main/resources/lang/lang_en.xml
+++ b/src/main/resources/lang/lang_en.xml
@@ -223,13 +223,19 @@
Rotary knob with rotary encoder. Used to detect rotational movements.
encoder signal A
encoder signal B
+
Keyboard
A keyboard that can be used to enter text.
- This component buffers the inputs, which can then be read out.
+ This component buffers the input, which can then be read out.
A separate window is opened for the text input.
- If high the output D is active and one keystroke is output.
- The last pressed key, or zero if no key is typed.
+ Clock. A rising edge removes the oldest character from the buffer.
+ If high the output D is active and one character is output.
+ It also enables the clock input.
+ The last typed character, or zero if no character is available.
+ This output indicates that characters are available.
+ It can be used to trigger an interrupt.
+
Terminal
You can write ASCII characters to this terminal.
The terminal opens its own window to visualize the output.
@@ -1417,6 +1423,7 @@
If this is not the case, the error message of the external application is displayed.
If possible, the input and output definitions are also adapted to the current code.
Executable file "{0}" not found!
+ Enter Text!
Ok
0°
diff --git a/src/test/resources/dig/backtrack/AllComponents.dig b/src/test/resources/dig/backtrack/AllComponents.dig
index 40393f6e4..18ae86ac0 100644
--- a/src/test/resources/dig/backtrack/AllComponents.dig
+++ b/src/test/resources/dig/backtrack/AllComponents.dig
@@ -170,7 +170,7 @@
Terminal
-
+
VDD
@@ -575,6 +575,10 @@
+
+
+
+
@@ -619,10 +623,6 @@
-
-
-
-
@@ -776,7 +776,7 @@
-
+
@@ -923,6 +923,10 @@
+
+
+
+
@@ -959,10 +963,6 @@
-
-
-
-
@@ -995,6 +995,10 @@
+
+
+
+
@@ -1276,8 +1280,8 @@
-
-
+
+
@@ -1305,12 +1309,16 @@
-
+
+
+
+
+
@@ -1653,7 +1661,7 @@
-
+