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 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 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 @@ - +