diff --git a/src/main/java/de/neemann/digital/core/element/Keys.java b/src/main/java/de/neemann/digital/core/element/Keys.java index ea0c0e10d..d629a7e68 100644 --- a/src/main/java/de/neemann/digital/core/element/Keys.java +++ b/src/main/java/de/neemann/digital/core/element/Keys.java @@ -452,7 +452,7 @@ public final class Keys { * enables the grid */ public static final Key SETTINGS_GRID - = new Key<>("grid", false); + = new Key<>("grid", true); /** * enables the wire bits view @@ -786,6 +786,14 @@ public final class Keys { * Circuit is generic */ public static final Key IS_GENERIC = - new Key("isGeneric", false).setSecondary(); + new Key<>("isGeneric", false).setSecondary(); + + + /** + * Enables the tutorial + */ + public static final Key SETTINGS_SHOW_TUTORIAL = + new Key<>("showTutorial", true).setSecondary(); + } diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java index 9d34eef7f..10e57a62a 100644 --- a/src/main/java/de/neemann/digital/gui/Main.java +++ b/src/main/java/de/neemann/digital/gui/Main.java @@ -43,6 +43,7 @@ import de.neemann.digital.gui.components.testing.TestAllDialog; import de.neemann.digital.gui.components.testing.ValueTableDialog; import de.neemann.digital.gui.components.tree.LibraryTreeModel; import de.neemann.digital.gui.components.tree.SelectTree; +import de.neemann.digital.gui.tutorial.InitialTutorial; import de.neemann.digital.gui.release.CheckForNewRelease; import de.neemann.digital.gui.remote.DigitalHandler; import de.neemann.digital.gui.remote.RemoteException; @@ -1863,6 +1864,10 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS if (file != null) builder.setFileToOpen(file); SwingUtilities.invokeLater(() -> { + final boolean tutorial = Settings.getInstance().getAttributes().get(Keys.SETTINGS_SHOW_TUTORIAL); + if (tutorial) + builder.setCircuit(new Circuit()); + Main main = builder.build(); try { new RemoteSever(new DigitalHandler(main)).start(41114); @@ -1871,6 +1876,9 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS } main.setVisible(true); + if (tutorial) + new InitialTutorial(main).setVisible(true); + CheckForNewRelease.showReleaseDialog(main); }); } diff --git a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java index f1e1465c6..d69702540 100644 --- a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java +++ b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java @@ -133,6 +133,7 @@ public class CircuitComponent extends JComponent implements ChangedListener, Lib private Mouse mouse = Mouse.getMouse(); private Circuit shallowCopy; private CircuitScrollPanel circuitScrollPanel; + private TutorialListener tutorialListener; /** @@ -429,6 +430,8 @@ public class CircuitComponent extends JComponent implements ChangedListener, Lib try { if (modification != null) { undoManager.apply(modification); + if (tutorialListener != null) + tutorialListener.modified(modification); if (circuitScrollPanel != null) circuitScrollPanel.sizeChanged(); } @@ -620,6 +623,9 @@ public class CircuitComponent extends JComponent implements ChangedListener, Lib getCircuit().clearState(); } requestFocusInWindow(); + + if (tutorialListener != null) + tutorialListener.modified(null); } /** @@ -1548,7 +1554,7 @@ public class CircuitComponent extends JComponent implements ChangedListener, Lib } private void insertWires(VisualElement element) { - if (element.isAutoWireCompatible()) { + if (element.isAutoWireCompatible() && tutorialListener == null) { Modifications.Builder wires = new Modifications.Builder<>(Lang.get("lib_wires")); for (Pin p : element.getPins()) insertWirePin(p, element.getRotate(), wires); @@ -2292,6 +2298,8 @@ public class CircuitComponent extends JComponent implements ChangedListener, Lib SwingUtilities.convertPointToScreen(p, CircuitComponent.this); boolean modelHasChanged = actor.interact(CircuitComponent.this, p, getPosVector(e), modelSync); if (modelHasChanged) { + if (tutorialListener != null) + tutorialListener.modified(null); modelHasChanged(); } else graphicHasChanged(); @@ -2317,6 +2325,15 @@ public class CircuitComponent extends JComponent implements ChangedListener, Lib new MouseControllerWizard(wizardNotification).activate(); } + /** + * Sets the modification listener. + * + * @param tutorialListener is called every time the circuit is modified + */ + public void setTutorialListener(TutorialListener tutorialListener) { + this.tutorialListener = tutorialListener; + } + /** * Deactivate a wizard */ @@ -2369,4 +2386,15 @@ public class CircuitComponent extends JComponent implements ChangedListener, Lib void closed(); } + /** + * Listener to get notified if the circuit has changed + */ + public interface TutorialListener { + /** + * Called if the circuit was modified + * + * @param modification the modification + */ + void modified(Modification modification); + } } diff --git a/src/main/java/de/neemann/digital/gui/tutorial/InitialTutorial.java b/src/main/java/de/neemann/digital/gui/tutorial/InitialTutorial.java new file mode 100644 index 000000000..2d7579a15 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/tutorial/InitialTutorial.java @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2019 Helmut Neemann. + * Use of this source code is governed by the GPL v3 license + * that can be found in the LICENSE file. + */ +package de.neemann.digital.gui.tutorial; + +import de.neemann.digital.core.*; +import de.neemann.digital.core.basic.XOr; +import de.neemann.digital.core.element.Element; +import de.neemann.digital.core.element.ElementTypeDescription; +import de.neemann.digital.core.element.Keys; +import de.neemann.digital.core.io.In; +import de.neemann.digital.core.io.Out; +import de.neemann.digital.draw.elements.Circuit; +import de.neemann.digital.draw.elements.PinException; +import de.neemann.digital.draw.elements.VisualElement; +import de.neemann.digital.draw.library.ElementNotFoundException; +import de.neemann.digital.draw.model.ModelCreator; +import de.neemann.digital.gui.Main; +import de.neemann.digital.gui.Settings; +import de.neemann.digital.gui.components.CircuitComponent; +import de.neemann.digital.gui.components.modification.ModifyInsertWire; +import de.neemann.digital.lang.Lang; +import de.neemann.digital.undo.Modification; +import de.neemann.digital.undo.Modifications; +import de.neemann.gui.LineBreaker; +import de.neemann.gui.Screen; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +/** + * The tutorial dialog. + */ +public class InitialTutorial extends JDialog implements CircuitComponent.TutorialListener { + private static final ArrayList STEPS = new ArrayList<>(); + + static { + STEPS.add(new Step("tutorial1", (cc, mod, t) -> contains(cc, In.DESCRIPTION))); + STEPS.add(new Step("tutorial2", (cc, mod, t) -> contains(cc, In.DESCRIPTION, In.DESCRIPTION))); + STEPS.add(new Step("tutorial3", (cc, mod, t) -> contains(cc, In.DESCRIPTION, In.DESCRIPTION, XOr.DESCRIPTION))); + STEPS.add(new Step("tutorial4", (cc, mod, t) -> contains(cc, In.DESCRIPTION, In.DESCRIPTION, XOr.DESCRIPTION, Out.DESCRIPTION))); + STEPS.add(new Step("tutorial5", (cc, mod, t) -> contains(mod, ModifyInsertWire.class))); + STEPS.add(new Step("tutorial6", (cc, mod, t) -> isWorking(cc))); + STEPS.add(new Step("tutorial7", (cc, mod, t) -> t.main.getModel() != null)); + STEPS.add(new Step("tutorial8", (cc, mod, t) -> outputIsHigh(t))); + STEPS.add(new Step("tutorial9", (cc, mod, t) -> t.main.getModel() == null)); + STEPS.add(new Step("tutorial10", (cc, mod, t) -> isIONamed(cc, 1, t))); + STEPS.add(new Step("tutorial11", (cc, mod, t) -> isIONamed(cc, 3, t))); + } + + private final Main main; + + private static boolean outputIsHigh(InitialTutorial t) { + Model model = t.main.getModel(); + if (model == null) + return false; + List nl = model.getNodes(); + if (nl.size() != 1) + return false; + + Node n = nl.get(0); + if (n instanceof Element) { + Element e = (Element) n; + try { + final ObservableValues outputs = e.getOutputs(); + if (outputs.size() != 1) + return false; + else + return outputs.get(0).getValue() != 0; + } catch (PinException ex) { + return false; + } + } else + return false; + } + + private static boolean isIONamed(CircuitComponent cc, int expected, InitialTutorial t) { + HashSet set = new HashSet<>(); + int num = 0; + for (VisualElement ve : cc.getCircuit().getElements()) { + if (ve.equalsDescription(In.DESCRIPTION) || ve.equalsDescription(Out.DESCRIPTION)) { + String l = ve.getElementAttributes().getLabel(); + if (!l.isEmpty()) { + if (set.contains(l)) { + t.setTextByID("tutorialUniqueIdents"); + return false; + } + set.add(l); + num++; + } + } + } + return num == expected; + } + + private static boolean isWorking(CircuitComponent cc) { + try { + new ModelCreator(cc.getCircuit(), cc.getLibrary()).createModel(false); + return true; + } catch (PinException | NodeException | ElementNotFoundException e) { + return false; + } + } + + private static boolean contains(Modification mod, Class modifyClass) { + if (mod == null) + return false; + if (mod.getClass() == modifyClass) + return true; + if (mod instanceof Modifications) { + Modifications m = (Modifications) mod; + for (Object i : m.getModifications()) + if (i.getClass() == modifyClass) + return true; + } + return false; + } + + private static boolean contains(CircuitComponent cc, ElementTypeDescription... descriptions) { + ArrayList el = new ArrayList<>(cc.getCircuit().getElements()); + if (el.size() != descriptions.length) + return false; + for (ElementTypeDescription d : descriptions) { + Iterator it = el.iterator(); + while (it.hasNext()) { + if (it.next().equalsDescription(d)) { + it.remove(); + break; + } + } + } + return el.isEmpty(); + } + + + private final JTextPane text; + private final CircuitComponent circuitComponent; + private int stepIndex; + + /** + * Creates the tutorial dialog. + * + * @param main the main class + */ + public InitialTutorial(Main main) { + super(main, Lang.get("tutorial"), false); + this.main = main; + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + setAlwaysOnTop(true); + circuitComponent = main.getCircuitComponent(); + circuitComponent.setTutorialListener(this); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosed(WindowEvent windowEvent) { + circuitComponent.setTutorialListener(null); + } + }); + + text = new JTextPane(); + text.setEditable(false); + text.setFont(Screen.getInstance().getFont(1.2f)); + text.setPreferredSize(new Dimension(300, 400)); + + getContentPane().add(new JScrollPane(text)); + getContentPane().add(new JButton(new AbstractAction(Lang.get("tutorialNotNeeded")) { + @Override + public void actionPerformed(ActionEvent actionEvent) { + disableTutorial(); + } + }), BorderLayout.SOUTH); + + pack(); + + final Point ml = main.getLocation(); + setLocation(ml.x - getWidth(), ml.y); + + stepIndex = -1; + incIndex(); + + } + + private void disableTutorial() { + Settings.getInstance().getAttributes().set(Keys.SETTINGS_SHOW_TUTORIAL, false); + dispose(); + } + + private void incIndex() { + stepIndex++; + if (stepIndex == STEPS.size()) { + disableTutorial(); + } else { + setTextByID(STEPS.get(stepIndex).getId()); + } + } + + private void setTextByID(String id) { + final String s = Lang.get(id); + text.setText(new LineBreaker(1000).breakLines(s)); + } + + @Override + public void modified(Modification modification) { + if (STEPS.get(stepIndex).getChecker().accomplished(circuitComponent, modification, this)) + incIndex(); + } + + private static final class Step { + private final String id; + private final Checker checker; + + private Step(String id, Checker checker) { + this.id = id; + this.checker = checker; + } + + public String getId() { + return id; + } + + public Checker getChecker() { + return checker; + } + } + + private interface Checker { + boolean accomplished(CircuitComponent circuitComponent, Modification modification, InitialTutorial t); + } +} diff --git a/src/main/java/de/neemann/digital/gui/tutorial/package-info.java b/src/main/java/de/neemann/digital/gui/tutorial/package-info.java new file mode 100644 index 000000000..cc6bdd7cf --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/tutorial/package-info.java @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2019 Helmut Neemann. + * Use of this source code is governed by the GPL v3 license + * that can be found in the LICENSE file. + */ + +/** + * Classes used to show the initial tutorial + */ +package de.neemann.digital.gui.tutorial; diff --git a/src/main/java/de/neemann/digital/undo/Modifications.java b/src/main/java/de/neemann/digital/undo/Modifications.java index 06275a842..9779a5f08 100644 --- a/src/main/java/de/neemann/digital/undo/Modifications.java +++ b/src/main/java/de/neemann/digital/undo/Modifications.java @@ -27,6 +27,13 @@ public final class Modifications> implements Modification< m.modify(a); } + /** + * @return The contained modifications + */ + public ArrayList> getModifications() { + return modifications; + } + @Override public String toString() { return name; diff --git a/src/main/java/de/neemann/gui/LineBreaker.java b/src/main/java/de/neemann/gui/LineBreaker.java index aca30de18..9ed1cd413 100644 --- a/src/main/java/de/neemann/gui/LineBreaker.java +++ b/src/main/java/de/neemann/gui/LineBreaker.java @@ -82,21 +82,28 @@ public class LineBreaker { StringBuilder word = new StringBuilder(); pos = indent; + boolean lastLineBreak=false; for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); switch (c) { case '\n': - if (preserveLineBreaks) { + if (preserveLineBreaks || lastLineBreak) { addWord(word); lineBreak(); - break; + } else { + addWord(word); + lastLineBreak = true; } + break; case '\r': + case '\t': case ' ': addWord(word); + lastLineBreak = false; break; default: word.append(c); + lastLineBreak = false; } } addWord(word); diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index 62dcbef39..ef0d4fb91 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -1349,6 +1349,9 @@ Sind evtl. die Namen der Variablen nicht eindeutig? Schaltung ist generisch Erlaubt die Erzeugung von generischen Schaltungen. + Tutorial beim Start anzeigen + Aktiviert das Tutorial. + Leitung eingefügt. Aus Zwischenablage eingefügt. Wert ''{0}'' in Element ''{1}'' verändert. @@ -1953,4 +1956,64 @@ Daher steht auch das Signal 'D_out' zur Verfügung, um in diesem Fall den Wert z Gibt es aus einem Zustand keinen unbedingten Übergang, bleibt der Automat in diesem Zustand, wenn keine andere Übergangsbedingung erfüllt ist. ]]> + + Tutorial + Im Folgenden führt Sie ein kurzes Tutorial zur ersten, + einfachen Schaltung: + + Beginnen Sie, indem Sie einen Eingang in die Schaltung einfügen. + Sie finden den Eingang im Menu Bauteile▸IO. + Fügen Sie nun einen zweiten Eingang in die Schaltung ein. + Sie können auch auf den Eingang in der Toolbar klicken. + + Setzen Sie den zweiten Eingang am besten mit etwas Abstand unter den ersten Eingang. + Sie können die Schaltung verschieben, wenn Sie die rechte Maustaste gedrückt halten. + Durch klicken auf Bauteile können Sie diese verschieben. + + Als nächstes soll ein "Exklusiv Oder" Gatter eingefügt werden. + Sie finden dieses Gatter im Menu Bauteile▸Logisch. + Setzen Sie dieses Bauteil mit etwas Abstand rechts neben die Eingänge. + + Als letztes Bauteil soll noch ein Ausgang eingefügt werden. + Platzieren Sie den Ausgang etwas rechts des "Exklusiv Oder" Gatters. + + Um die Schaltung zu vervollständigen, sind Verbindungsleitungen zu ziehen. + + Klicken Sie auf den roten Punkt am ersten Eingang und verbinden Sie diesen mit einem Eingang + des "Exklusiv Oder" Gatters, indem Sie danach auf einen blauen Punkt des "Exklusiv Oder" + Gatters klicken. Die Maustaste NICHT gedrückt halten! + + Verbinden Sie den roten Punkt des zweiten Eingangs mit den zweiten blauen + Punkten des "Exklusiv Oder" Gatters und den roten Punkt des "Exklusiv Oder" Gatters mit dem + blauen Punkt des Ausgangs. + + Während Sie die Leitung zeichnen, können Sie sie durch Klicken auf die Arbeitsfläche anheften. + Rechts-Klick bricht das Zeichnen der Leitung ab (Steuerung-Klick unter MacOS). + + Damit ist Ihre erste Schaltung funktionsfähig. + Um die Simulation zu starten, können Sie auf den Play-Knopf in der Toolbar klicken. + Wenn Sie mit der Maus über die Toolbar fahren, werden Tool-Tipps angezeigt. + + + Die Simulation ist jetzt aktiv. + Schalten Sie die Eingänge um, indem Sie darauf klicken. + + + Um die Simulation zu beenden, klicken Sie auf den Stop-Knopf in der Toolbar. + + + Der Vollständigkeit halber sollen die Ein- und Ausgänge benannt werden. + + Durch Rechts-Klick auf einen Eingang öffnet sich ein Dialog (Unter MacOS wird + Steuerung-Klick verwendet). Hier kann der Eingang mit einer Bezeichnung versehen werden. + + + Benennen Sie alle Ein- und Ausgänge. + + + Ein- und Ausgänge sollten stets eindeutig benannt sein. + + + Tutorial überspringen + diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index 57d26e567..4a99c25dd 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -129,7 +129,7 @@ - Out + Output Can be used to display an output signal in a circuit. This element is also used to connect a circuit to an embedding circuit. In this case the connection is bidirectional. @@ -1335,6 +1335,9 @@ Circuit is generic Allows to create a generic circuit. + Show Tutorial at Startup + Enables the tutorial. + Inserted wire. Insert from clipboard. Value ''{0}'' in component ''{1}'' modified. @@ -1922,4 +1925,56 @@ Therefore, the signal 'D_out' is also available to check the value in this case. transition condition is met. ]]> + Tutorial + In the following a short tutorial leads you to the first, simple circuit: + + First, insert an input into the circuit. You will find the input in the menu Components▸IO. + Now add a second input to the circuit. You can also click on the input + in the toolbar. + + It is best to place the second input slightly below the first input. + You can move the circuit by holding down the right mouse button. + By clicking on components you can move them. + Next, an "Exclusive Or" gate is to be inserted. + You can find this gate in the menu Components▸Logic. + Place this component with some distance to the right of the inputs. + The last component to be inserted is an output. + Place it with some distance to the right of the "Exclusive Or" gate. + + In order to complete the circuit, connecting wires must be drawn. + + Click on the red dot at the first input and connect it to an input of the "Exclusive Or" gate, + by clicking on a blue dot of the "Exclusive Or" gate afterwards. + Do NOT drag with mouse button down! + + Connect the red dot of the second input to the second blue dot of the + "Exclusive Or" gate and the red dot of the "Exclusive Or" gate to the blue dot of the output. + + While drawing, you can pin the wire by clicking somewhere on the canvas. + Right-click cancels the drawing of the wire (control-click on MacOS). + + Your first circuit is now functional. + To start the simulation, you can click on the Play button in the toolbar. + If you move the mouse over the toolbar, tool tips are shown. + + + The simulation is now active. Switch the inputs by clicking on them. + + + To stop the simulation, click on the Stop button in the toolbar. + + + For completeness, the inputs and outputs should be labeled. + + Right-click on an input to open a dialog. On MacOS control-click is used. + Here the input can be given a name. + + Label all inputs and outputs. + + + Inputs and outputs should always be uniquely named. + + + Skip Tutorial + \ No newline at end of file diff --git a/src/main/resources/lang/lang_es.xml b/src/main/resources/lang/lang_es.xml index 587253675..f91653fe0 100644 --- a/src/main/resources/lang/lang_es.xml +++ b/src/main/resources/lang/lang_es.xml @@ -241,7 +241,8 @@ Divisor Divide o crea un haz de cables o un bus de datos con más de un bit. Con un bus es posible, por ejemplo, generar conexiones de 16 bits sin tener que generar 16 cables individuales. - Las 16 conexiones pueden agruparse en un cable. + Las 16 conexiones pueden agruparse en un cable. + El divisor tiene una dirección, es decir, sólo puede transmitir señales en una dirección. Los bits de entrada {0} El bit de entrada {0} Los bits de salida {0} @@ -716,7 +717,8 @@ Se necesita un componente de reloj. Todos los flip-flops deben usar una señal de reloj. Este circuito no tiene entradas etiquetadas. Este circuito no tiene salidas etiquetadas. - Después de {0} ciclos, no se produjo ninguna interrupción en el punto de interrupción ''{1}''. + Después de {0} ciclos, no se produjo ninguna interrupción en el punto de interrupción ''{1}''. + Posiblemente el número de ciclos de tiempo muerto en el componente de interrupción deberían incrementarse. Expresión {0} no soportada. Operación {0} no soportada. Error creando la tabla de consulta (LUT). @@ -1257,7 +1259,7 @@ Guardar los datos en archivo CSV Test de velocidad Hace un test de velocidad calculando la frecuencia de reloj máxima. - Paso + Paso de puerta Calcula un paso de puerta simple Sintetizar Genera la expresión booleana mínima a partir de una tabla de verdad. @@ -1723,4 +1725,55 @@ Por tanto, la señal 'D_out' estará también disponible para chequear el valor Error al leer la configuración de la toolchain {0} ¡El comando "{0}" ha empezado! ¡El procesado puede llevar un tiempo! ¡El comando "{0}" ha sido completado! + Hay una entrada o una salida sin nombre + La señal "{0}" no es válida o se ha usado varias veces + Error al sustituir componentes para el análisis. + Error en la evaluación del código genérico del circuito. Código {1} en el Componente: {0} + Error al analizar código genérico + Parametrización genérica + Declaraciones hechas para generar un circuito + El circuito es genérico + Permite crear un circuito genérico + Muestra el tutorial en el arranque + Habilita el tutorial + Entradas + Bits + Bits de dirección + Tutorial + A continuación, podrás diseñar tu primer circuito tras un breve tutorial: + + Primero, pon una entrada en el circuito. Encontrarás las entradas en el menú Componentes▸Entrada-Salida + Ahora, añade una segunda entrada al circuito. También puedes hacer clic sobre la entrada que ha aparecido en la barra de herramientas. + + Es mejor colocar la segunda entrada a dos espacios de rejilla debajo de la primera. + Puedes mover el circuito presionando el botón derecho del ratón. + Haciendo clic sobre las entradas puedes moverlas. + A continuación vamos a insertar una puerta "OR Exclusiva". + Puedes encontrarla en el menú Componentes▸Lógica. + Coloca el componente a dos espacios de rejilla a la derecha de las entradas. + El último componente que vamos a insertar es una salida. + Colócala a dos espacios de rejilla a la derecha de la puerta "OR Exclusiva". + Para completar el circuito hay que dibujar los cables. + + Haz clic en el punto rojo de la primera entrada y conecta el cable que aparece a una entrada de la puerta "OR Exclusiva" + haciendo clic en uno de los puntos azules de dicha puerta. + ¡NO pinches y arrastres con el botón del ratón! + Conecta los puntos rojos de las entradas con los puntos azules de la puerta "OR Exclusiva" + y el punto rojo de dicha puerta con el punto azul de la salida. + Así de fácil: un punto rojo va a un punto azul. + Tu primer circuito ya funcionará. + Para empezar la simulación, puedes hacer clic en el botón "Simular" de la barra de herramientas. + La simulación ya está activa. Ahora puedes cambiar los valores de las entradas haciendo clic en ellas. + Para terminar la simulación, haz clic en el botón "Stop" de la barra de herramientas. + Para que todo quede perfecto, tendrías que poner etiquetas a las entradas y salidas. + + Haz clic con el botón derecho en una entrada para abrir un cuadro de diálogo. + En MacOS puedes usar el control-clic. + Así podrás nombrar la entrada. + Etiqueta todas las entradas y salidas. + Los nombres de las entradas y salidas deben ser únicos. + Saltar el tutorial + Ejecutar hasta parar en modo de puerta simple. + Ejecuta todos los pasos de puerta sencillos hasta que se detecta un flanco de subida en un componente de parada. + Si no hay componente de parada, se ejecutan los pasos de puerta simple restantes. diff --git a/src/main/resources/lang/lang_es_ref.xml b/src/main/resources/lang/lang_es_ref.xml index ab5317f09..48f4d8a59 100644 --- a/src/main/resources/lang/lang_es_ref.xml +++ b/src/main/resources/lang/lang_es_ref.xml @@ -111,7 +111,7 @@ This component can be used to realize any necessary propagation delay. Input of the signal to be delayed. The input signal delayed by one gate delay time. - Out + Output Can be used to display an output signal in a circuit. This element is also used to connect a circuit to an embedding circuit. In this case the connection is bidirectional. @@ -246,7 +246,8 @@ Splitter Splits or creates a wire bundle or a data bus with more than one bit. With a bus it is e.g. possible to generate 16-bit connections without having to route 16 individual wires. - All 16 connections can be merged into one wire. + All 16 connections can be merged into one wire. + The splitter has a direction, meaning it can only transmit signals in one direction. The input bits {0}. The input bit {0}. The output bits {0}. @@ -725,7 +726,8 @@ A single clock component is necessary. All flip-flops must use this clock signal. The circuit has no labeled inputs The circuit has no labeled outputs - No break detected after {0} cycles at break point ''{1}''. + No break detected after {0} cycles at break point ''{1}''. + Possibly the number of timout cycles in the break component should be increased. Expression {0} not supported Operation {0} not supported Error creating the lookup table. @@ -1270,8 +1272,8 @@ Save data as CSV file Speed Test Performs a speed test by calculating the max. clock frequency. - Step - Calculating a single gate step + Gate Step + Calculates a single gate step Synthesise Generates the minimal bool expressions described by a truth table. {0} variables @@ -1739,4 +1741,60 @@ Therefore, the signal 'D_out' is also available to check the value in this case. Error while reading the toolchain configuration {0} Command "{0}" has been started! Processing may take some time! The command "{0}" has been completed! + There is a unnamed input or output! + The signal name "{0}" is invalid or used multiple times! + Error when substituting components for the analysis. + Error in the evaluation of the generic code of the circuit. Code + {1} + at Component: {0} + Error while parsing generics code. + Generic Parameterization + Statements used to generify a circuit. + Circuit is generic + Allows to create a generic circuit. + Show Tutorial at Startup + Enables the tutorial. + Inputs + Bits + Addr. Bits + Tutorial + In the following a short tutorial leads you to the first, simple circuit: + + First, insert an input into the circuit. You will find the input in the menu Components▸IO. + Now add a second input to the circuit. You can also click on the input + in the toolbar. + + It is best to place the second input slightly below the first input. + You can move the circuit by holding down the right mouse button. + By clicking on components you can move them. + Next, an "Exclusive Or" gate is to be inserted. + You can find this gate in the menu Components▸Logic. + Place this component with some distance to the right of the inputs. + The last component to be inserted is an output. + Place it with some distance to the right of the "Exclusive Or" gate. + In order to complete the circuit, connecting wires must be drawn. + + Click on the red dot at the first input and connect it to an input of the "Exclusive Or" gate, + by clicking on a blue dot of the "Exclusive Or" gate afterwards. + Do NOT drag with mouse button down! + Connect the red dot of the second input to the second blue dot of the + "Exclusive Or" gate and the red dot of the "Exclusive Or" gate to the blue dot of the output. + + While drawing, you can pin the wire by clicking somewhere on the canvas. + Right-click cancels the drawing of the wire (control-click on MacOS). + Your first circuit is now functional. + To start the simulation, you can click on the Play button in the toolbar. + If you move the mouse over the toolbar, tool tips are shown. + The simulation is now active. Switch the inputs by clicking on them. + To stop the simulation, click on the Stop button in the toolbar. + For completeness, the inputs and outputs should be labeled. + + Right-click on an input to open a dialog. On MacOS control-click is used. + Here the input can be given a name. + Label all inputs and outputs. + Inputs and outputs should always be uniquely named. + Skip Tutorial + Run To Break in Single Gate Mode + Executes all single gate steps until a rising edge is detected on a break component. + If there is no break component, the remaining single gate steps are executed. diff --git a/src/main/resources/lang/lang_pt_ref.xml b/src/main/resources/lang/lang_pt_ref.xml index 318b2ee96..d9cc9ae5e 100644 --- a/src/main/resources/lang/lang_pt_ref.xml +++ b/src/main/resources/lang/lang_pt_ref.xml @@ -114,7 +114,7 @@ Input of the signal to be delayed. The input signal delayed by one gate delay time. - Out + Output Can be used to display an output signal in a circuit. This element is also used to connect a circuit to an embedding circuit. In this case the connection is bidirectional. diff --git a/src/test/java/de/neemann/digital/lang/TestLang.java b/src/test/java/de/neemann/digital/lang/TestLang.java index 83a567b41..df7dbe76f 100644 --- a/src/test/java/de/neemann/digital/lang/TestLang.java +++ b/src/test/java/de/neemann/digital/lang/TestLang.java @@ -68,7 +68,7 @@ public class TestLang extends TestCase { StringBuilder sb = new StringBuilder(); for (String key : map.keySet()) { if (!keys.contains(key)) { - if (!(key.startsWith("key_") || key.startsWith("elem_"))) { + if (!(key.startsWith("key_") || key.startsWith("elem_") || key.startsWith("tutorial"))) { if (sb.length() > 0) sb.append(", "); sb.append('"').append(key).append('"'); diff --git a/src/test/java/de/neemann/gui/LineBreakerTest.java b/src/test/java/de/neemann/gui/LineBreakerTest.java index 8a727ae6a..adf492bfe 100644 --- a/src/test/java/de/neemann/gui/LineBreakerTest.java +++ b/src/test/java/de/neemann/gui/LineBreakerTest.java @@ -12,8 +12,8 @@ import junit.framework.TestCase; public class LineBreakerTest extends TestCase { public void testBreakLines() throws Exception { - assertEquals("this is a test string", new LineBreaker(60).breakLines("this \n\n is \n a test \n\r string")); - assertEquals("this is a test\nstring", new LineBreaker(14).breakLines("this \n\n is \n a test \n\r string")); + assertEquals("this\nis a test string", new LineBreaker(60).breakLines("this \n\n is \n a test \n\r string")); + assertEquals("this\nis a test\nstring", new LineBreaker(14).breakLines("this \n\n is \n a test \n\r string")); assertEquals("This is a test string. This\n" + "is a test string. This is a\n" + "test string.", new LineBreaker(27).breakLines("This is a test string. This is a test string. This is a test string."));