diff --git a/README.md b/README.md
index dfb9ceaa3..e6b20fcdf 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ This are the main features of Digital:
- You can test your design by creating test cases and execute them.
- Many examples: From a transmission gate D-flipflop to a complete (simple) MIPS-like processor.
- Fast-run mode to perform a simulation without updating the HMI.
- A simple processor can be clocked at 3MHz.
+ A simple processor can be clocked at 100kHz.
- Displaying a LST file when executing assembler programs within such a processor.
- Simple remote TCP interface to allow e.g. an assembler IDE to control the simulator.
- SVG export of circuits, including a LaTeX-compatible SVG version (see [ctan](https://www.ctan.org/tex-archive/info/svg-inkscape))
@@ -105,7 +105,7 @@ Logisim works somewhat different, which sometimes leads to surprises like unexpe
If a complete processors is simulated, it is possible to calculate the simulation without an update of the
graphical representation.
-A simple processor (see example) can so simulated with a roughly 3MHz clock (Intel® Core ™ i5-3230M CPU @ 2.60GHz)
+A simple processor (see example) can so simulated with a roughly 100kHz clock (Intel® Core ™ i5-3230M CPU @ 2.60GHz)
which is suitable also for more complex exercises.
There is a break gate having a single input. If this input changes from low to high this quick run is stopped.
In this way, an assembler instruction BRK can be implemented, which then can be used to insert break points
diff --git a/README_de.md b/README_de.md
index be81c7e8c..29387e9b0 100644
--- a/README_de.md
+++ b/README_de.md
@@ -26,7 +26,7 @@ Folgende Features zeichnen Digital aus:
- Analyse und Synthese von kombinatorischen Schaltungen und Schaltwerken.
- Viele Beispiele: Vom Transmision-Gate D-FF bis zum kompletten MIPS-ähnlichem Prozessor.
- Fast-Run-Mode um eine Simulation ohne Aktualisierung des HMI durchzuführen.
- Ein einfacher Prozessor kann mit 3MHz getaktet werden.
+ Ein einfacher Prozessor kann mit 100kHz getaktet werden.
- Anzeige von LST-Files bei der Ausführung von Assembler-Programmen.
- Einfache Remote TCP-Schnittstelle um z.B. mit einer Assembler-IDE den Simulator zu steuern.
- SVG-Export von Schaltungen, incl. einer LaTeX-tauglichen SVG-Variante (siehe [ctan](ftp://ftp.fau.de/ctan/info/svg-inkscape/InkscapePDFLaTeX.pdf))
@@ -97,7 +97,7 @@ Logisim arbeitet hier etwas anders, was gelegentlich zu Überraschungen führt,
### Performance ###
Werden komplette Prozessoren simuliert, ist es möglich die Simulation zu berechnen ohne die grafische Anzeige zu aktualisieren.
-Ein einfacher Prozessor (siehe Beispiel) lässt sich so mit etwa 3MHz takten (Intel® Core™ i5-3230M CPU @ 2.60GHz) was auch für
+Ein einfacher Prozessor (siehe Beispiel) lässt sich so mit etwa 100kHz takten (Intel® Core™ i5-3230M CPU @ 2.60GHz) was auch für
komplexere Übungen ausreichend ist.
Es gibt ein Break-Gatter welches einen einzelnen Eingang hat. Wechselt dieser Eingang von low auf high wird dieser
schnelle Lauf beendet. Auf diese Weise lässt sich eine Assembler-Anweisung BRK implementieren, womit sich dann Break-Points
diff --git a/src/main/dig/processor/Graphics.dig b/src/main/dig/processor/Graphics.dig
index 4317552b6..2537d8491 100644
--- a/src/main/dig/processor/Graphics.dig
+++ b/src/main/dig/processor/Graphics.dig
@@ -14,7 +14,7 @@
16
-
+
In
@@ -24,7 +24,7 @@
R
-
+
Comparator
@@ -101,7 +101,7 @@
C
-
+
And
@@ -160,6 +160,10 @@
D_FF
+
+ valueIsProbe
+ true
+
Label
Bank-FF
@@ -175,7 +179,7 @@
W
-
+
Splitter
@@ -256,7 +260,7 @@
-
+
@@ -264,7 +268,7 @@
-
+
@@ -292,7 +296,7 @@
-
+
@@ -324,11 +328,11 @@
-
-
+
+
-
+
@@ -336,7 +340,7 @@
-
+
@@ -348,7 +352,7 @@
-
+
@@ -367,6 +371,14 @@
+
+
+
+
+
+
+
+
@@ -395,14 +407,6 @@
-
-
-
-
-
-
-
-
diff --git a/src/main/dig/processor/Processor.dig b/src/main/dig/processor/Processor.dig
index 6ca3cb418..d56db3d74 100644
--- a/src/main/dig/processor/Processor.dig
+++ b/src/main/dig/processor/Processor.dig
@@ -11,7 +11,7 @@
-
+
Register.dig
@@ -155,26 +155,139 @@
Data
- 13
- 5137
- 5120
+ 126
+ 32768
+ 4960
+ 33168
+ 4944
+ 5264
+ 5251
+ 5408
+ 5179
+ 8417
+ 5372
+ 19695
+ 28266
+ 6177
+ 8417
+ 5617
+ 19695
+ 28261
+ 8225
+ 6193
+ 8417
+ 5623
+ 19695
+ 28255
+ 8225
+ 8417
+ 32797
+ 4848
+ 19695
+ 28249
+ 6177
+ 6193
+ 8417
+ 32804
+ 4848
+ 19695
+ 28242
+ 28190
+ 517
+ 534
+ 5168
+ 5152
+ 5280
+ 32840
+ 26816
+ 31360
+ 15488
+ 24069
+ 15474
+ 24069
+ 15475
+ 24067
+ 28163
+ 15475
+ 25601
+ 5283
+ 29722
6145
- 15888
- 29521
- 15375
- 26107
+ 6161
+ 6177
+ 15652
+ 26092
+ 6193
+ 15668
+ 26088
+ 12433
+ 65535
+ 28681
+ 582
+ 613
+ 596
+ 31744
+ 28637
+ 5232
+ 8241
+ 8452
+ 32877
+ 26800
+ 6177
+ 6145
+ 32877
+ 26800
+ 6193
+ 6404
+ 32877
+ 26800
+ 6193
+ 6404
+ 32877
+ 26800
+ 8225
8193
- 16400
- 29521
- 15360
- 26107
- 28661
+ 32877
+ 26800
+ 8225
+ 8193
+ 32877
+ 26800
+ 8241
+ 8452
+ 32877
+ 26800
+ 8241
+ 8452
+ 32877
+ 26800
+ 6177
+ 6193
+ 6405
+ 27148
+ 15652
+ 25094
+ 15668
+ 25092
+ 31360
+ 15488
+ 24065
+ 6257
+ 27147
+ 579
+ 14148
+ 1090
+ 1094
+ 29768
+ 20222
+ 6369
+ 27151
16
lastDataFile
- /home/hneemann/Dokumente/Java/assembler/assembler3/src/main/asm/lightChase.hex
+ /home/hneemann/Dokumente/Java/assembler/assembler3/src/main/asm/Conway2.hex
@@ -191,8 +304,12 @@
Bits
16
+
+ flipSelPos
+ true
+
-
+
PC.dig
@@ -212,7 +329,7 @@
Frequency
- 50
+ 500000
@@ -225,7 +342,7 @@
-
+
LED
@@ -235,7 +352,7 @@
-
+
LED
@@ -245,7 +362,7 @@
-
+
LED
@@ -255,7 +372,7 @@
-
+
LED
@@ -265,7 +382,7 @@
-
+
LED
@@ -275,7 +392,7 @@
-
+
LED
@@ -285,7 +402,7 @@
-
+
LED
@@ -295,7 +412,7 @@
-
+
LED
@@ -305,7 +422,7 @@
-
+
LED
@@ -315,7 +432,7 @@
-
+
LED
@@ -325,7 +442,7 @@
-
+
LED
@@ -335,7 +452,7 @@
-
+
LED
@@ -345,7 +462,7 @@
-
+
LED
@@ -355,7 +472,7 @@
-
+
LED
@@ -365,7 +482,7 @@
-
+
LED
@@ -375,7 +492,7 @@
-
+
LED
@@ -385,7 +502,7 @@
-
+
Driver
@@ -441,7 +558,7 @@
stPC
-
+
Tunnel
@@ -455,7 +572,7 @@
WE
-
+
Tunnel
@@ -469,7 +586,7 @@
imm
-
+
Tunnel
@@ -483,7 +600,7 @@
abs
-
+
Tunnel
@@ -497,7 +614,7 @@
muxB
-
+
Tunnel
@@ -511,7 +628,7 @@
src2D
-
+
Tunnel
@@ -525,7 +642,7 @@
muxA
-
+
Tunnel
@@ -539,7 +656,7 @@
ALU2D
-
+
Tunnel
@@ -553,7 +670,7 @@
ALUop
-
+
Tunnel
@@ -567,7 +684,7 @@
sf
-
+
Tunnel
@@ -581,7 +698,7 @@
br
-
+
Tunnel
@@ -595,7 +712,7 @@
st
-
+
Tunnel
@@ -609,7 +726,7 @@
ld
-
+
Tunnel
@@ -623,7 +740,7 @@
ioW
-
+
Tunnel
@@ -637,21 +754,21 @@
ioR
-
+
Tunnel
rotation
-
+
NetName
stPC
-
+
Tunnel
@@ -817,7 +934,7 @@
ioW
-
+
Tunnel
@@ -883,7 +1000,7 @@
C
-
+
Tunnel
@@ -912,7 +1029,7 @@
/home/hneemann/Dokumente/Java/digital/src/main/dig/processor/Terminal.dig
-
+
Tunnel
@@ -926,7 +1043,7 @@
C
-
+
Tunnel
@@ -940,12 +1057,12 @@
ioW
-
+
/home/hneemann/Dokumente/Java/digital/src/main/dig/processor/Graphics.dig
-
+
Tunnel
@@ -959,7 +1076,7 @@
ioW
-
+
Tunnel
@@ -973,7 +1090,7 @@
ioR
-
+
Tunnel
@@ -987,69 +1104,73 @@
C
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
@@ -1059,10 +1180,6 @@
-
-
-
-
@@ -1075,6 +1192,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1111,18 +1244,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1131,6 +1252,10 @@
+
+
+
+
@@ -1140,8 +1265,20 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1168,16 +1305,16 @@
-
-
+
+
-
-
+
+
-
-
+
+
@@ -1193,7 +1330,7 @@
-
+
@@ -1221,32 +1358,12 @@
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1259,6 +1376,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1268,8 +1397,8 @@
-
-
+
+
@@ -1279,10 +1408,6 @@
-
-
-
-
@@ -1304,20 +1429,8 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
@@ -1328,7 +1441,15 @@
-
+
+
+
+
+
+
+
+
+
@@ -1337,7 +1458,7 @@
-
+
@@ -1359,10 +1480,6 @@
-
-
-
-
@@ -1372,20 +1489,24 @@
-
-
+
+
-
-
+
+
+
+
+
+
-
-
+
+
@@ -1408,40 +1529,36 @@
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
+
+
@@ -1452,24 +1569,28 @@
-
-
-
-
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
@@ -1479,10 +1600,6 @@
-
-
-
-
@@ -1497,11 +1614,15 @@
-
+
-
-
+
+
+
+
+
+
@@ -1524,8 +1645,8 @@
-
-
+
+
@@ -1535,61 +1656,49 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
diff --git a/src/main/java/de/neemann/digital/analyse/TruthTableTableModel.java b/src/main/java/de/neemann/digital/analyse/TruthTableTableModel.java
index 891b4cfce..4f9b15a21 100644
--- a/src/main/java/de/neemann/digital/analyse/TruthTableTableModel.java
+++ b/src/main/java/de/neemann/digital/analyse/TruthTableTableModel.java
@@ -17,7 +17,7 @@ public class TruthTableTableModel implements TableModel {
public static final String[] STATENAMES = new String[]{"0", "1", "x"};
private final TruthTable truthTable;
- private ArrayList listeners = new ArrayList<>();
+ private final ArrayList listeners = new ArrayList<>();
/**
* Creates a new instance
diff --git a/src/main/java/de/neemann/digital/core/SpeedTest.java b/src/main/java/de/neemann/digital/core/SpeedTest.java
index b92a5de50..b750a63ca 100644
--- a/src/main/java/de/neemann/digital/core/SpeedTest.java
+++ b/src/main/java/de/neemann/digital/core/SpeedTest.java
@@ -49,6 +49,7 @@ public class SpeedTest {
for (int i = 0; i < LOOPCOUNTER; i++) {
state = 1 - state;
clockValue.setValue(state);
+ model.doStep();
}
loops++;
aktTime = System.currentTimeMillis();
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 3d549ebb3..60369e680 100644
--- a/src/main/java/de/neemann/digital/core/element/Keys.java
+++ b/src/main/java/de/neemann/digital/core/element/Keys.java
@@ -77,7 +77,7 @@ public final class Keys {
*/
public static final Key FREQUENCY
= new Key.KeyInteger("Frequency", 1)
- .setComboBoxValues(new Integer[]{1, 2, 5, 10, 20, 50, 100, 200, 500})
+ .setComboBoxValues(new Integer[]{1, 2, 5, 10, 20, 50, 100, 200, 500, 5000, 50000, 500000})
.setMin(1);
/**
* the bit count of a muxer or decoder
diff --git a/src/main/java/de/neemann/digital/core/memory/DataField.java b/src/main/java/de/neemann/digital/core/memory/DataField.java
index c45d54a95..6545628a3 100644
--- a/src/main/java/de/neemann/digital/core/memory/DataField.java
+++ b/src/main/java/de/neemann/digital/core/memory/DataField.java
@@ -20,7 +20,7 @@ public class DataField {
private long[] data;
private final int bits;
- private transient ArrayList listeners;
+ private final transient ArrayList listeners = new ArrayList<>();
/**
* Creates a new DataField
@@ -158,9 +158,9 @@ public class DataField {
* @param l the listener
*/
public void addListener(DataListener l) {
- if (listeners == null)
- listeners = new ArrayList<>();
- listeners.add(l);
+ synchronized (listeners) {
+ listeners.add(l);
+ }
}
/**
@@ -169,12 +169,9 @@ public class DataField {
* @param l the listener to remove
*/
public void removeListener(DataListener l) {
- if (listeners == null)
- return;
-
- listeners.remove(l);
- if (listeners.isEmpty())
- listeners = null;
+ synchronized (listeners) {
+ listeners.remove(l);
+ }
}
/**
@@ -183,11 +180,10 @@ public class DataField {
* @param addr the address which value has changed
*/
public void fireChanged(int addr) {
- if (listeners == null)
- return;
-
- for (DataListener l : listeners)
- l.valueChanged(addr);
+ synchronized (listeners) {
+ for (DataListener l : listeners)
+ l.valueChanged(addr);
+ }
}
/**
diff --git a/src/main/java/de/neemann/digital/draw/elements/VisualElement.java b/src/main/java/de/neemann/digital/draw/elements/VisualElement.java
index 8cf6a447a..e720235b4 100644
--- a/src/main/java/de/neemann/digital/draw/elements/VisualElement.java
+++ b/src/main/java/de/neemann/digital/draw/elements/VisualElement.java
@@ -6,6 +6,7 @@ import de.neemann.digital.draw.graphics.*;
import de.neemann.digital.draw.shapes.*;
import de.neemann.digital.draw.shapes.Shape;
import de.neemann.digital.gui.components.CircuitComponent;
+import de.neemann.digital.gui.sync.Sync;
import javax.swing.*;
import java.awt.*;
@@ -261,9 +262,9 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
* @param pos the position
* @return true if model is changed
*/
- public boolean elementClicked(CircuitComponent cc, Point pos) {
+ public boolean elementClicked(CircuitComponent cc, Point pos, Sync modelSync) {
if (interactor != null)
- return interactor.clicked(cc, pos, ioState, element);
+ return interactor.clicked(cc, pos, ioState, element, modelSync);
else
return false;
}
@@ -276,9 +277,9 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
* @param pos the position
* @return true if model is changed
*/
- public boolean elementPressed(CircuitComponent cc, Point pos) {
+ public boolean elementPressed(CircuitComponent cc, Point pos, Sync modelSync) {
if (interactor != null)
- return interactor.pressed(cc, pos, ioState, element);
+ return interactor.pressed(cc, pos, ioState, element, modelSync);
else
return false;
}
@@ -291,9 +292,9 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
* @param pos the position
* @return true if model is changed
*/
- public boolean elementReleased(CircuitComponent cc, Point pos) {
+ public boolean elementReleased(CircuitComponent cc, Point pos, Sync modelSync) {
if (interactor != null)
- return interactor.released(cc, pos, ioState, element);
+ return interactor.released(cc, pos, ioState, element, modelSync);
else
return false;
}
diff --git a/src/main/java/de/neemann/digital/draw/model/RealTimeClock.java b/src/main/java/de/neemann/digital/draw/model/RealTimeClock.java
index b85ce18f1..92d79f697 100644
--- a/src/main/java/de/neemann/digital/draw/model/RealTimeClock.java
+++ b/src/main/java/de/neemann/digital/draw/model/RealTimeClock.java
@@ -4,24 +4,28 @@ import de.neemann.digital.core.*;
import de.neemann.digital.core.wiring.Clock;
import de.neemann.digital.gui.ErrorStopper;
import de.neemann.digital.gui.GuiModelObserver;
+import de.neemann.digital.gui.StatusInterface;
+import de.neemann.digital.gui.sync.Sync;
import de.neemann.digital.lang.Lang;
-import javax.swing.*;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* The real time clock which is used to fire the models clocks with realtime signals
+ *
* @author hneemann
*/
public class RealTimeClock implements ModelStateObserver {
private final Model model;
private final ScheduledThreadPoolExecutor executor;
private final ErrorStopper stopper;
+ private final Sync modelSync;
+ private final StatusInterface status;
private final int frequency;
private final ObservableValue output;
- private ScheduledFuture> timer;
+ private Runner runner;
/**
* Creates a new real time clock
@@ -29,11 +33,14 @@ public class RealTimeClock implements ModelStateObserver {
* @param model the model
* @param clock the clock element which is modify
* @param executor the executor used to schedule the update
+ * @param status allows sending messages to the status line
*/
- public RealTimeClock(Model model, Clock clock, ScheduledThreadPoolExecutor executor, ErrorStopper stopper) {
+ public RealTimeClock(Model model, Clock clock, ScheduledThreadPoolExecutor executor, ErrorStopper stopper, Sync modelSync, StatusInterface status) {
this.model = model;
this.executor = executor;
this.stopper = stopper;
+ this.modelSync = modelSync;
+ this.status = status;
int f = clock.getFrequency();
if (f < 1) f = 1;
this.frequency = f;
@@ -44,27 +51,96 @@ public class RealTimeClock implements ModelStateObserver {
public void handleEvent(ModelEvent event) {
switch (event) {
case STARTED:
- if (frequency > 50) // if frequency is high it is not necessary to update the GUI at every clock
- output.removeObserver(GuiModelObserver.class);
+ if (frequency > 50) // if frequency is high it is not necessary to update the GUI at every clock change
+ modelSync.access(() -> output.removeObserver(GuiModelObserver.class));
- int delay = 500 / frequency;
- if (delay < 1) delay = 1;
+ int delay = 500000 / frequency;
+ if (delay < 10)
+ runner = new ThreadRunner();
+ else
+ runner = new RealTimeRunner(delay);
+ break;
+ case STOPPED:
+ if (runner != null)
+ runner.stop();
+ break;
+ }
+ }
- timer = executor.scheduleAtFixedRate(() -> SwingUtilities.invokeLater(() -> {
- output.setValue(1 - output.getValue());
+ interface Runner {
+ void stop();
+ }
+
+ /**
+ * runs with defined rate
+ */
+ private class RealTimeRunner implements Runner {
+
+ private final ScheduledFuture> timer;
+
+ RealTimeRunner(int delay) {
+ timer = executor.scheduleAtFixedRate(new Runnable() {
+ @Override
+ public void run() {
try {
- model.doStep();
+ modelSync.accessNEx(() -> {
+ output.setValue(1 - output.getValue());
+ model.doStep();
+ });
} catch (NodeException e1) {
stopper.showErrorAndStopModel(Lang.get("msg_clockError"), e1);
timer.cancel(false);
}
- }), delay, delay, TimeUnit.MILLISECONDS);
+ }
+ }, delay, delay, TimeUnit.MICROSECONDS);
+ }
- break;
- case STOPPED:
- if (timer != null)
- timer.cancel(false);
- break;
+ @Override
+ public void stop() {
+ if (timer != null)
+ timer.cancel(false);
+ }
+ }
+
+ /**
+ * runs at fast as possible!
+ */
+ private class ThreadRunner implements Runner {
+
+ private final Thread thread;
+
+ ThreadRunner() {
+ thread = new Thread() {
+ @Override
+ public void run() {
+ System.out.println("thread start");
+ long time = System.currentTimeMillis();
+ long counter = 0;
+ try {
+ while (!interrupted()) {
+ modelSync.accessNEx(() -> {
+ output.setValue(1 - output.getValue());
+ model.doStep();
+ });
+ counter++;
+ }
+ } catch (NodeException e1) {
+ stopper.showErrorAndStopModel(Lang.get("msg_clockError"), e1);
+ }
+ time = System.currentTimeMillis() - time;
+
+ status.setStatus(counter / time / 2 + "kHz");
+
+ System.out.println("thread end");
+ }
+ };
+ thread.setDaemon(true);
+ thread.start();
+ }
+
+ @Override
+ public void stop() {
+ thread.interrupt();
}
}
}
diff --git a/src/main/java/de/neemann/digital/draw/shapes/ButtonShape.java b/src/main/java/de/neemann/digital/draw/shapes/ButtonShape.java
index fc36f678c..c58d41934 100644
--- a/src/main/java/de/neemann/digital/draw/shapes/ButtonShape.java
+++ b/src/main/java/de/neemann/digital/draw/shapes/ButtonShape.java
@@ -11,6 +11,7 @@ import de.neemann.digital.draw.elements.Pins;
import de.neemann.digital.draw.graphics.*;
import de.neemann.digital.draw.graphics.Polygon;
import de.neemann.digital.gui.components.CircuitComponent;
+import de.neemann.digital.gui.sync.Sync;
import java.awt.*;
@@ -52,21 +53,25 @@ public class ButtonShape implements Shape {
ioState.getOutput(0).addObserverToValue(guiObserver);
return new InteractorInterface() {
@Override
- public boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element) {
+ public boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element, Sync modelSync) {
return false;
}
@Override
- public boolean pressed(CircuitComponent cc, Point pos, IOState ioState, Element element) {
- ObservableValue value = ioState.getOutput(0);
- value.setValue(1);
+ public boolean pressed(CircuitComponent cc, Point pos, IOState ioState, Element element, Sync modelSync) {
+ modelSync.access(() -> {
+ ObservableValue value = ioState.getOutput(0);
+ value.setValue(1);
+ });
return true;
}
@Override
- public boolean released(CircuitComponent cc, Point pos, IOState ioState, Element element) {
- ObservableValue value = ioState.getOutput(0);
- value.setValue(0);
+ public boolean released(CircuitComponent cc, Point pos, IOState ioState, Element element, Sync modelSync) {
+ modelSync.access(() -> {
+ ObservableValue value = ioState.getOutput(0);
+ value.setValue(0);
+ });
return true;
}
};
diff --git a/src/main/java/de/neemann/digital/draw/shapes/ClockShape.java b/src/main/java/de/neemann/digital/draw/shapes/ClockShape.java
index 06a29e333..759d9d172 100644
--- a/src/main/java/de/neemann/digital/draw/shapes/ClockShape.java
+++ b/src/main/java/de/neemann/digital/draw/shapes/ClockShape.java
@@ -11,6 +11,7 @@ import de.neemann.digital.draw.elements.Pins;
import de.neemann.digital.draw.graphics.*;
import de.neemann.digital.draw.graphics.Polygon;
import de.neemann.digital.gui.components.CircuitComponent;
+import de.neemann.digital.gui.sync.Sync;
import java.awt.*;
@@ -18,6 +19,7 @@ import static de.neemann.digital.draw.shapes.OutputShape.SIZE;
/**
* The Clock shape
+ *
* @author hneemann
*/
public class ClockShape implements Shape {
@@ -49,10 +51,12 @@ public class ClockShape implements Shape {
ioState.getOutput(0).addObserverToValue(guiObserver); // necessary to replot wires also if component itself does not depend on state
return new Interactor() {
@Override
- public boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element) {
+ public boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element, Sync modelSync) {
ObservableValue value = ioState.getOutput(0);
if (value.getBits() == 1) {
- value.setValue(1 - value.getValue());
+ modelSync.access(() -> {
+ value.setValue(1 - value.getValue());
+ });
return true;
}
return false;
diff --git a/src/main/java/de/neemann/digital/draw/shapes/DataShape.java b/src/main/java/de/neemann/digital/draw/shapes/DataShape.java
index 19be37911..b68ed948b 100644
--- a/src/main/java/de/neemann/digital/draw/shapes/DataShape.java
+++ b/src/main/java/de/neemann/digital/draw/shapes/DataShape.java
@@ -16,6 +16,7 @@ import de.neemann.digital.gui.components.CircuitComponent;
import de.neemann.digital.gui.components.OrderMerger;
import de.neemann.digital.gui.components.data.DataSet;
import de.neemann.digital.gui.components.data.DataSetObserver;
+import de.neemann.digital.gui.sync.Sync;
import java.awt.*;
import java.util.ArrayList;
@@ -52,7 +53,7 @@ public class DataShape implements Shape {
public Interactor applyStateMonitor(IOState ioState, Observer guiObserver) {
return new Interactor() {
@Override
- public boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element) {
+ public boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element, Sync modelSync) {
dataSet.clear();
return false;
}
diff --git a/src/main/java/de/neemann/digital/draw/shapes/InputShape.java b/src/main/java/de/neemann/digital/draw/shapes/InputShape.java
index 26ad76ab3..ce64a3057 100644
--- a/src/main/java/de/neemann/digital/draw/shapes/InputShape.java
+++ b/src/main/java/de/neemann/digital/draw/shapes/InputShape.java
@@ -12,6 +12,7 @@ import de.neemann.digital.draw.graphics.*;
import de.neemann.digital.draw.graphics.Polygon;
import de.neemann.digital.gui.components.CircuitComponent;
import de.neemann.digital.gui.components.SingleValueDialog;
+import de.neemann.digital.gui.sync.Sync;
import java.awt.*;
@@ -52,17 +53,19 @@ public class InputShape implements Shape {
ioState.getOutput(0).addObserverToValue(guiObserver);
return new Interactor() {
@Override
- public boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element) {
+ public boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element, Sync modelSync) {
ObservableValue value = ioState.getOutput(0);
if (value.getBits() == 1) {
- if (value.supportsHighZ()) {
- if (value.isHighZ()) value.set(0, false);
- else if (value.getValue() == 0) value.setValue(1);
- else value.set(0, true);
- } else
- value.setValue(1 - value.getValue());
+ modelSync.access(() -> {
+ if (value.supportsHighZ()) {
+ if (value.isHighZ()) value.set(0, false);
+ else if (value.getValue() == 0) value.setValue(1);
+ else value.set(0, true);
+ } else
+ value.setValue(1 - value.getValue());
+ });
} else {
- SingleValueDialog.editValue(pos, value);
+ SingleValueDialog.editValue(pos, value, modelSync);
}
return true;
}
diff --git a/src/main/java/de/neemann/digital/draw/shapes/Interactor.java b/src/main/java/de/neemann/digital/draw/shapes/Interactor.java
index a4241c528..b01db8646 100644
--- a/src/main/java/de/neemann/digital/draw/shapes/Interactor.java
+++ b/src/main/java/de/neemann/digital/draw/shapes/Interactor.java
@@ -3,6 +3,7 @@ package de.neemann.digital.draw.shapes;
import de.neemann.digital.core.element.Element;
import de.neemann.digital.draw.elements.IOState;
import de.neemann.digital.gui.components.CircuitComponent;
+import de.neemann.digital.gui.sync.Sync;
import java.awt.*;
@@ -17,12 +18,12 @@ import java.awt.*;
public abstract class Interactor implements InteractorInterface {
@Override
- public boolean pressed(CircuitComponent cc, Point pos, IOState ioState, Element element) {
+ public boolean pressed(CircuitComponent cc, Point pos, IOState ioState, Element element, Sync modelSync) {
return false;
}
@Override
- public boolean released(CircuitComponent cc, Point pos, IOState ioState, Element element) {
+ public boolean released(CircuitComponent cc, Point pos, IOState ioState, Element element, Sync modelSync) {
return false;
}
}
diff --git a/src/main/java/de/neemann/digital/draw/shapes/InteractorInterface.java b/src/main/java/de/neemann/digital/draw/shapes/InteractorInterface.java
index fcda42143..83ce3ed51 100644
--- a/src/main/java/de/neemann/digital/draw/shapes/InteractorInterface.java
+++ b/src/main/java/de/neemann/digital/draw/shapes/InteractorInterface.java
@@ -3,6 +3,7 @@ package de.neemann.digital.draw.shapes;
import de.neemann.digital.core.element.Element;
import de.neemann.digital.draw.elements.IOState;
import de.neemann.digital.gui.components.CircuitComponent;
+import de.neemann.digital.gui.sync.Sync;
import java.awt.*;
@@ -23,7 +24,7 @@ public interface InteractorInterface {
* @param ioState the state of the element
* @return true if model is changed
*/
- boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element);
+ boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element, Sync modelSync);
/**
* Called mouse is pressed on running model
@@ -33,7 +34,7 @@ public interface InteractorInterface {
* @param ioState the state of the element
* @return true if model is changed
*/
- boolean pressed(CircuitComponent cc, Point pos, IOState ioState, Element element);
+ boolean pressed(CircuitComponent cc, Point pos, IOState ioState, Element element, Sync modelSync);
/**
* Called mouse is released on running model
@@ -43,5 +44,5 @@ public interface InteractorInterface {
* @param ioState the state of the element
* @return true if model is changed
*/
- boolean released(CircuitComponent cc, Point pos, IOState ioState, Element element);
+ boolean released(CircuitComponent cc, Point pos, IOState ioState, Element element, Sync modelSync);
}
diff --git a/src/main/java/de/neemann/digital/draw/shapes/RAMShape.java b/src/main/java/de/neemann/digital/draw/shapes/RAMShape.java
index 4776bfc0b..cd0e1c07a 100644
--- a/src/main/java/de/neemann/digital/draw/shapes/RAMShape.java
+++ b/src/main/java/de/neemann/digital/draw/shapes/RAMShape.java
@@ -8,6 +8,7 @@ import de.neemann.digital.core.memory.RAMInterface;
import de.neemann.digital.draw.elements.IOState;
import de.neemann.digital.gui.components.CircuitComponent;
import de.neemann.digital.gui.components.DataEditor;
+import de.neemann.digital.gui.sync.Sync;
import java.awt.*;
@@ -32,10 +33,10 @@ public class RAMShape extends GenericShape {
public Interactor applyStateMonitor(IOState ioState, Observer guiObserver) {
return new Interactor() {
@Override
- public boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element) {
+ public boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element, Sync modelSync) {
if (element instanceof RAMInterface) {
DataField dataField = ((RAMInterface) element).getMemory();
- new DataEditor(cc, dataField).showDialog();
+ new DataEditor(cc, dataField, modelSync).showDialog();
}
return false;
}
diff --git a/src/main/java/de/neemann/digital/gui/GuiModelObserver.java b/src/main/java/de/neemann/digital/gui/GuiModelObserver.java
index 9723ed7cd..3cefd5997 100644
--- a/src/main/java/de/neemann/digital/gui/GuiModelObserver.java
+++ b/src/main/java/de/neemann/digital/gui/GuiModelObserver.java
@@ -5,6 +5,8 @@ import de.neemann.digital.core.ModelStateObserver;
import de.neemann.digital.core.Observer;
import de.neemann.digital.gui.components.CircuitComponent;
+import javax.swing.*;
+
/**
* This observer is added to the model if real time timers are started.
* This observer paints the CircuitComponent after a step is calculated.
@@ -18,6 +20,7 @@ public class GuiModelObserver implements Observer, ModelStateObserver {
private final CircuitComponent component;
private final ModelEvent type;
private boolean changed = false;
+ private volatile boolean paintPending;
/**
* Creates a new instance.
@@ -38,7 +41,13 @@ public class GuiModelObserver implements Observer, ModelStateObserver {
@Override
public void handleEvent(ModelEvent event) {
if (changed && event == type) {
- component.paintImmediately(0, 0, component.getWidth(), component.getHeight());
+ if (!paintPending) {
+ paintPending = true;
+ SwingUtilities.invokeLater(() -> {
+ paintPending = false;
+ component.paintImmediately(0, 0, component.getWidth(), component.getHeight());
+ });
+ }
changed = false;
}
}
diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java
index 75a8a8c19..c1931d4c1 100644
--- a/src/main/java/de/neemann/digital/gui/Main.java
+++ b/src/main/java/de/neemann/digital/gui/Main.java
@@ -34,6 +34,9 @@ import de.neemann.digital.gui.remote.DigitalHandler;
import de.neemann.digital.gui.remote.RemoteSever;
import de.neemann.digital.gui.state.State;
import de.neemann.digital.gui.state.StateManager;
+import de.neemann.digital.gui.sync.LockSync;
+import de.neemann.digital.gui.sync.NoSync;
+import de.neemann.digital.gui.sync.Sync;
import de.neemann.digital.lang.Lang;
import de.neemann.gui.*;
@@ -56,7 +59,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
*
* @author hneemann
*/
-public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, ErrorStopper, FileHistory.OpenInterface, DigitalRemoteInterface {
+public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, ErrorStopper, FileHistory.OpenInterface, DigitalRemoteInterface, StatusInterface {
private static final ArrayList ATTR_LIST = new ArrayList<>();
private static boolean experimental;
@@ -106,7 +109,9 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
private File filename;
private FileHistory fileHistory;
+ private Sync modelSync;
private Model model;
+
private ModelCreator modelCreator;
private boolean realtimeClockRunning;
@@ -615,7 +620,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
public void enter() {
super.enter();
clearModelDescription();
- circuitComponent.setModeAndReset(false);
+ circuitComponent.setModeAndReset(false, NoSync.INST);
doStep.setEnabled(false);
runToBreakAction.setEnabled(false);
}
@@ -662,7 +667,6 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
private boolean createAndStartModel(boolean globalRunClock, ModelEvent updateEvent) {
try {
circuitComponent.removeHighLighted();
- circuitComponent.setModeAndReset(true);
modelCreator = new ModelCreator(circuitComponent.getCircuit(), library);
@@ -674,12 +678,19 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
statusLabel.setText(Lang.get("msg_N_nodes", model.size()));
realtimeClockRunning = false;
+ modelSync = null;
if (globalRunClock)
for (Clock c : model.getClocks())
if (c.getFrequency() > 0) {
- model.addObserver(new RealTimeClock(model, c, timerExecuter, this));
+ if (modelSync == null)
+ modelSync = new LockSync();
+ model.addObserver(new RealTimeClock(model, c, timerExecuter, this, modelSync, this));
realtimeClockRunning = true;
}
+ if (modelSync == null)
+ modelSync = NoSync.INST;
+
+ circuitComponent.setModeAndReset(true, modelSync);
if (realtimeClockRunning) {
// if clock is running, enable automatic update of gui
@@ -695,12 +706,12 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
List ordering = circuitComponent.getCircuit().getMeasurementOrdering();
if (settings.get(Keys.SHOW_DATA_TABLE))
- windowPosManager.register("probe", new ProbeDialog(this, model, updateEvent, ordering)).setVisible(true);
+ windowPosManager.register("probe", new ProbeDialog(this, model, updateEvent, ordering, modelSync)).setVisible(true);
if (settings.get(Keys.SHOW_DATA_GRAPH))
- windowPosManager.register("dataset", new DataSetDialog(this, model, updateEvent == ModelEvent.MICROSTEP, ordering)).setVisible(true);
+ windowPosManager.register("dataset", new DataSetDialog(this, model, updateEvent == ModelEvent.MICROSTEP, ordering, modelSync)).setVisible(true);
if (settings.get(Keys.SHOW_DATA_GRAPH_MICRO))
- windowPosManager.register("datasetMicro", new DataSetDialog(this, model, true, ordering)).setVisible(true);
+ windowPosManager.register("datasetMicro", new DataSetDialog(this, model, true, ordering, modelSync)).setVisible(true);
int i = 0;
for (ROM rom : model.findNode(ROM.class))
@@ -830,6 +841,11 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
return windowPosManager;
}
+ @Override
+ public void setStatus(String message) {
+ SwingUtilities.invokeLater(() -> statusLabel.setText(message));
+ }
+
private class FullStepObserver implements Observer {
private final Model model;
@@ -840,8 +856,10 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
@Override
public void hasChanged() {
try {
- model.fireManualChangeEvent();
- model.doStep();
+ modelSync.accessNEx(() -> {
+ model.fireManualChangeEvent();
+ model.doStep();
+ });
circuitComponent.repaint();
} catch (NodeException | RuntimeException e) {
showErrorAndStopModel(Lang.get("msg_errorCalculatingStep"), e);
diff --git a/src/main/java/de/neemann/digital/gui/StatusInterface.java b/src/main/java/de/neemann/digital/gui/StatusInterface.java
new file mode 100644
index 000000000..0f73606b0
--- /dev/null
+++ b/src/main/java/de/neemann/digital/gui/StatusInterface.java
@@ -0,0 +1,15 @@
+package de.neemann.digital.gui;
+
+/**
+ * Interface to acess the status line
+ *
+ * @author hneemann
+ */
+public interface StatusInterface {
+ /**
+ * Set message to the status line
+ *
+ * @param message the message
+ */
+ void setStatus(String message);
+}
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 291d8cbc4..fe7d1ad19 100644
--- a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java
+++ b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java
@@ -13,6 +13,8 @@ import de.neemann.digital.draw.shapes.ShapeFactory;
import de.neemann.digital.gui.LibrarySelector;
import de.neemann.digital.gui.Main;
import de.neemann.digital.gui.SavedListener;
+import de.neemann.digital.gui.sync.NoSync;
+import de.neemann.digital.gui.sync.Sync;
import de.neemann.digital.lang.Lang;
import de.neemann.gui.IconCreator;
import de.neemann.gui.ToolTipAction;
@@ -73,6 +75,7 @@ public class CircuitComponent extends JComponent {
private AffineTransform transform = new AffineTransform();
private Observer manualChangeObserver;
private Vector lastMousePos;
+ private Sync modelSync;
/**
@@ -248,13 +251,15 @@ public class CircuitComponent extends JComponent {
*
* @param runMode true if running, false if editing
*/
- public void setModeAndReset(boolean runMode) {
+ public void setModeAndReset(boolean runMode, Sync modelSync) {
+ this.modelSync = modelSync;
if (runMode)
mouseRun.activate();
- else
+ else {
mouseNormal.activate();
+ circuit.clearState();
+ }
requestFocusInWindow();
- circuit.clearState();
}
/**
@@ -372,7 +377,7 @@ public class CircuitComponent extends JComponent {
public void setCircuit(Circuit circuit) {
this.circuit = circuit;
fitCircuit();
- setModeAndReset(false);
+ setModeAndReset(false, NoSync.INST);
}
/**
@@ -982,7 +987,7 @@ public class CircuitComponent extends JComponent {
private interface Actor {
- boolean interact(CircuitComponent cc, Point p);
+ boolean interact(CircuitComponent cc, Point p, Sync modelSync);
}
private final class MouseControllerRun extends MouseController {
@@ -1016,7 +1021,7 @@ public class CircuitComponent extends JComponent {
private void interact(MouseEvent e, Actor actor) {
Point p = new Point(e.getX(), e.getY());
SwingUtilities.convertPointToScreen(p, CircuitComponent.this);
- boolean modelHasChanged = actor.interact(CircuitComponent.this, p);
+ boolean modelHasChanged = actor.interact(CircuitComponent.this, p, modelSync);
if (modelHasChanged) {
if (manualChangeObserver != null)
manualChangeObserver.hasChanged();
diff --git a/src/main/java/de/neemann/digital/gui/components/DataEditor.java b/src/main/java/de/neemann/digital/gui/components/DataEditor.java
index 9fca08411..4b6adbd1d 100644
--- a/src/main/java/de/neemann/digital/gui/components/DataEditor.java
+++ b/src/main/java/de/neemann/digital/gui/components/DataEditor.java
@@ -3,6 +3,7 @@ package de.neemann.digital.gui.components;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.Keys;
import de.neemann.digital.core.memory.DataField;
+import de.neemann.digital.gui.sync.Sync;
import de.neemann.digital.lang.Lang;
import javax.swing.*;
@@ -32,8 +33,8 @@ public class DataEditor extends JDialog {
* @param parent the parent
* @param dataField the data to edit
*/
- public DataEditor(JComponent parent, DataField dataField) {
- this(parent, dataField, null);
+ public DataEditor(JComponent parent, DataField dataField, Sync modelSync) {
+ this(parent, dataField, null, modelSync);
}
/**
@@ -43,7 +44,7 @@ public class DataEditor extends JDialog {
* @param dataField the data to edit
* @param attr uset to get bit sizes
*/
- public DataEditor(JComponent parent, DataField dataField, ElementAttributes attr) {
+ public DataEditor(JComponent parent, DataField dataField, ElementAttributes attr, Sync modelSync) {
super(SwingUtilities.windowForComponent(parent), Lang.get("key_Data"), attr == null ? ModalityType.MODELESS : ModalityType.APPLICATION_MODAL);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
@@ -70,7 +71,7 @@ public class DataEditor extends JDialog {
if (size <= 16) cols = 1;
else if (size <= 128) cols = 8;
- MyTableModel dm = new MyTableModel(this.dataField, cols);
+ MyTableModel dm = new MyTableModel(this.dataField, cols, modelSync);
JTable table = new JTable(dm);
table.setDefaultRenderer(MyLong.class, new MyLongRenderer(bits));
getContentPane().add(new JScrollPane(table));
@@ -122,12 +123,14 @@ public class DataEditor extends JDialog {
private final static class MyTableModel implements TableModel, DataField.DataListener {
private final DataField dataField;
private final int cols;
+ private final Sync modelSync;
private final int rows;
private ArrayList listener = new ArrayList<>();
- private MyTableModel(DataField dataField, int cols) {
+ private MyTableModel(DataField dataField, int cols, Sync modelSync) {
this.dataField = dataField;
this.cols = cols;
+ this.modelSync = modelSync;
rows = (dataField.size() - 1) / cols + 1;
}
@@ -171,7 +174,9 @@ public class DataEditor extends JDialog {
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
- dataField.setData(rowIndex * cols + (columnIndex - 1), ((MyLong) aValue).getValue());
+ modelSync.access(()->{
+ dataField.setData(rowIndex * cols + (columnIndex - 1), ((MyLong) aValue).getValue());
+ });
}
private void fireEvent(TableModelEvent e) {
diff --git a/src/main/java/de/neemann/digital/gui/components/EditorFactory.java b/src/main/java/de/neemann/digital/gui/components/EditorFactory.java
index 81530ef70..432ae3e34 100644
--- a/src/main/java/de/neemann/digital/gui/components/EditorFactory.java
+++ b/src/main/java/de/neemann/digital/gui/components/EditorFactory.java
@@ -8,6 +8,7 @@ import de.neemann.digital.core.memory.DataField;
import de.neemann.digital.core.memory.ROM;
import de.neemann.digital.gui.components.test.TestData;
import de.neemann.digital.gui.components.test.TestDataEditor;
+import de.neemann.digital.gui.sync.NoSync;
import de.neemann.digital.lang.Lang;
import de.neemann.gui.ErrorMessage;
import de.neemann.gui.ToolTipAction;
@@ -15,6 +16,7 @@ import de.neemann.gui.language.Bundle;
import de.neemann.gui.language.Language;
import javax.swing.*;
+import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.IOException;
@@ -234,7 +236,7 @@ public final class EditorFactory {
panel.add(new ToolTipAction(Lang.get("btn_edit")) {
@Override
public void actionPerformed(ActionEvent e) {
- DataEditor de = new DataEditor(panel, data, attr);
+ DataEditor de = new DataEditor(panel, data, attr, NoSync.INST);
if (de.showDialog()) {
data = de.getDataField();
}
@@ -245,6 +247,7 @@ public final class EditorFactory {
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser();
fc.setSelectedFile(attr.getFile(ROM.LAST_DATA_FILE_KEY));
+ fc.setFileFilter(new FileNameExtensionFilter("hex", "hex"));
if (fc.showOpenDialog(panel) == JFileChooser.APPROVE_OPTION) {
attr.setFile(ROM.LAST_DATA_FILE_KEY, fc.getSelectedFile());
try {
diff --git a/src/main/java/de/neemann/digital/gui/components/ProbeDialog.java b/src/main/java/de/neemann/digital/gui/components/ProbeDialog.java
index c6f135560..68e62e561 100644
--- a/src/main/java/de/neemann/digital/gui/components/ProbeDialog.java
+++ b/src/main/java/de/neemann/digital/gui/components/ProbeDialog.java
@@ -4,6 +4,7 @@ import de.neemann.digital.core.Model;
import de.neemann.digital.core.ModelEvent;
import de.neemann.digital.core.ModelStateObserver;
import de.neemann.digital.core.Signal;
+import de.neemann.digital.gui.sync.Sync;
import de.neemann.digital.lang.Lang;
import javax.swing.*;
@@ -27,12 +28,13 @@ public class ProbeDialog extends JDialog implements ModelStateObserver {
/**
* Creates a new instance
*
- * @param owner the owner
- * @param model the model to run
- * @param type the event type which fires a dialog repaint
- * @param ordering the names list used to order the measurement values
+ * @param owner the owner
+ * @param model the model to run
+ * @param type the event type which fires a dialog repaint
+ * @param ordering the names list used to order the measurement values
+ * @param modelSync
*/
- public ProbeDialog(Frame owner, Model model, ModelEvent type, List ordering) {
+ public ProbeDialog(Frame owner, Model model, ModelEvent type, List ordering, Sync modelSync) {
super(owner, Lang.get("win_measures"), false);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
this.type = type;
@@ -53,12 +55,12 @@ public class ProbeDialog extends JDialog implements ModelStateObserver {
addWindowListener(new WindowAdapter() {
@Override
public void windowOpened(WindowEvent e) {
- model.addObserver(ProbeDialog.this);
+ modelSync.access(() -> model.addObserver(ProbeDialog.this));
}
@Override
public void windowClosed(WindowEvent e) {
- model.removeObserver(ProbeDialog.this);
+ modelSync.access(() -> model.removeObserver(ProbeDialog.this));
}
});
@@ -71,7 +73,7 @@ public class ProbeDialog extends JDialog implements ModelStateObserver {
@Override
public void handleEvent(ModelEvent event) {
if (event == type || event == ModelEvent.MANUALCHANGE) {
- tableModel.fireChanged();
+ SwingUtilities.invokeLater(tableModel::fireChanged);
}
}
diff --git a/src/main/java/de/neemann/digital/gui/components/SingleValueDialog.java b/src/main/java/de/neemann/digital/gui/components/SingleValueDialog.java
index 95aaa853b..64c36a036 100644
--- a/src/main/java/de/neemann/digital/gui/components/SingleValueDialog.java
+++ b/src/main/java/de/neemann/digital/gui/components/SingleValueDialog.java
@@ -1,6 +1,7 @@
package de.neemann.digital.gui.components;
import de.neemann.digital.core.ObservableValue;
+import de.neemann.digital.gui.sync.Sync;
import de.neemann.digital.lang.Lang;
import javax.swing.*;
@@ -60,16 +61,20 @@ public final class SingleValueDialog extends JDialog {
* @param pos the position to pop up the dialog
* @param value the value to edit
*/
- public static void editValue(Point pos, ObservableValue value) {
+ public static void editValue(Point pos, ObservableValue value, Sync modelSync) {
String ret = new SingleValueDialog(pos, value.getValueString()).showDialog();
if (ret != null) {
ret = ret.trim();
if (ret.equals("?") && value.supportsHighZ()) {
- value.setHighZ(true);
+ modelSync.access(() -> {
+ value.setHighZ(true);
+ });
} else {
try {
long l = Long.decode(ret);
- value.set(l, false);
+ modelSync.access(() -> {
+ value.set(l, false);
+ });
} catch (NumberFormatException e) {
}
diff --git a/src/main/java/de/neemann/digital/gui/components/data/DataSet.java b/src/main/java/de/neemann/digital/gui/components/data/DataSet.java
index 8ee77fbc6..084a2001c 100644
--- a/src/main/java/de/neemann/digital/gui/components/data/DataSet.java
+++ b/src/main/java/de/neemann/digital/gui/components/data/DataSet.java
@@ -75,7 +75,7 @@ public class DataSet implements Iterable, Drawable {
*
* @param sample the DataSample
*/
- public void add(DataSample sample) {
+ synchronized void add(DataSample sample) {
while (samples.size() >= maxSize)
samples.remove(0);
@@ -154,7 +154,7 @@ public class DataSet implements Iterable, Drawable {
@Override
- public void drawTo(Graphic g, boolean highLight) {
+ synchronized public void drawTo(Graphic g, boolean highLight) {
int x = getTextBorder();
int yOffs = SIZE / 2;
diff --git a/src/main/java/de/neemann/digital/gui/components/data/DataSetDialog.java b/src/main/java/de/neemann/digital/gui/components/data/DataSetDialog.java
index eddbe02b8..dd6dded6b 100644
--- a/src/main/java/de/neemann/digital/gui/components/data/DataSetDialog.java
+++ b/src/main/java/de/neemann/digital/gui/components/data/DataSetDialog.java
@@ -5,6 +5,7 @@ import de.neemann.digital.core.ModelEvent;
import de.neemann.digital.core.ModelStateObserver;
import de.neemann.digital.core.Signal;
import de.neemann.digital.gui.components.OrderMerger;
+import de.neemann.digital.gui.sync.Sync;
import de.neemann.digital.lang.Lang;
import de.neemann.gui.ErrorMessage;
import de.neemann.gui.ToolTipAction;
@@ -29,6 +30,7 @@ public class DataSetDialog extends JDialog implements ModelStateObserver {
private static final int MAX_SAMPLE_SIZE = 1000;
private final DataSetComponent dsc;
private final JScrollPane scrollPane;
+ private final Sync modelSync;
private DataSet dataSet;
private DataSetObserver dataSetObserver;
@@ -40,8 +42,9 @@ public class DataSetDialog extends JDialog implements ModelStateObserver {
* @param microStep true the event type which triggers a new DataSample
* @param ordering the ordering of the measurement values
*/
- public DataSetDialog(Frame owner, Model model, boolean microStep, List ordering) {
+ public DataSetDialog(Frame owner, Model model, boolean microStep, List ordering, Sync modelSync) {
super(owner, createTitle(microStep), false);
+ this.modelSync = modelSync;
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setAlwaysOnTop(true);
@@ -64,12 +67,12 @@ public class DataSetDialog extends JDialog implements ModelStateObserver {
addWindowListener(new WindowAdapter() {
@Override
public void windowOpened(WindowEvent e) {
- model.addObserver(DataSetDialog.this);
+ modelSync.access(() -> model.addObserver(DataSetDialog.this));
}
@Override
public void windowClosed(WindowEvent e) {
- model.removeObserver(DataSetDialog.this);
+ modelSync.access(() -> model.removeObserver(DataSetDialog.this));
}
});
@@ -111,10 +114,14 @@ public class DataSetDialog extends JDialog implements ModelStateObserver {
@Override
public void handleEvent(ModelEvent event) {
- dataSetObserver.handleEvent(event);
- dsc.revalidate();
- dsc.repaint();
- JScrollBar bar = scrollPane.getHorizontalScrollBar();
- SwingUtilities.invokeLater(() -> bar.setValue(bar.getMaximum()));
+ modelSync.access(() -> {
+ dataSetObserver.handleEvent(event);
+ });
+ SwingUtilities.invokeLater(() -> {
+ dsc.revalidate();
+ dsc.repaint();
+ JScrollBar bar = scrollPane.getHorizontalScrollBar();
+ bar.setValue(bar.getMaximum());
+ });
}
}
diff --git a/src/main/java/de/neemann/digital/gui/components/graphics/GraphicCard.java b/src/main/java/de/neemann/digital/gui/components/graphics/GraphicCard.java
index d8ba1ca30..388a184f6 100644
--- a/src/main/java/de/neemann/digital/gui/components/graphics/GraphicCard.java
+++ b/src/main/java/de/neemann/digital/gui/components/graphics/GraphicCard.java
@@ -17,6 +17,10 @@ import javax.swing.*;
import static de.neemann.digital.core.element.PinInfo.input;
/**
+ * Graphic card.
+ * Mostly a RAM module with an additional input bit which selects the visible bank.
+ * So you can use double buffering.
+ *
* @author hneemann
*/
public class GraphicCard extends Node implements Element, RAMInterface {
diff --git a/src/main/java/de/neemann/digital/gui/components/listing/ROMListingDialog.java b/src/main/java/de/neemann/digital/gui/components/listing/ROMListingDialog.java
index f7d6fcaf9..f4bfdaa22 100644
--- a/src/main/java/de/neemann/digital/gui/components/listing/ROMListingDialog.java
+++ b/src/main/java/de/neemann/digital/gui/components/listing/ROMListingDialog.java
@@ -83,7 +83,6 @@ public class ROMListingDialog extends JDialog implements Observer {
list.setSelectedIndex(line);
});
}
-
lastAddr = addr;
}
}
diff --git a/src/main/java/de/neemann/digital/gui/sync/LockSync.java b/src/main/java/de/neemann/digital/gui/sync/LockSync.java
new file mode 100644
index 000000000..b624d6c18
--- /dev/null
+++ b/src/main/java/de/neemann/digital/gui/sync/LockSync.java
@@ -0,0 +1,41 @@
+package de.neemann.digital.gui.sync;
+
+import de.neemann.digital.core.NodeException;
+
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Calls the runnables under a reentrant lock.
+ *
+ * @author hneemann
+ */
+public class LockSync implements Sync {
+ private final ReentrantLock lock;
+
+ /**
+ * Creates a new instance
+ */
+ public LockSync() {
+ lock = new ReentrantLock();
+ }
+
+ @Override
+ public void access(Runnable run) {
+ lock.lock();
+ try {
+ run.run();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public void accessNEx(Sync.ModelRun run) throws NodeException {
+ lock.lock();
+ try {
+ run.run();
+ } finally {
+ lock.unlock();
+ }
+ }
+}
diff --git a/src/main/java/de/neemann/digital/gui/sync/NoSync.java b/src/main/java/de/neemann/digital/gui/sync/NoSync.java
new file mode 100644
index 000000000..545d17bde
--- /dev/null
+++ b/src/main/java/de/neemann/digital/gui/sync/NoSync.java
@@ -0,0 +1,29 @@
+package de.neemann.digital.gui.sync;
+
+import de.neemann.digital.core.NodeException;
+
+/**
+ * Implementation which is used in runtim clock does not run.
+ * Does no synchronisation at all.
+ *
+ * @author hneemann
+ */
+public final class NoSync implements Sync {
+ /**
+ * The single instance
+ */
+ public static final Sync INST = new NoSync();
+
+ private NoSync() {
+ }
+
+ @Override
+ public void access(Runnable run) {
+ run.run();
+ }
+
+ @Override
+ public void accessNEx(Sync.ModelRun run) throws NodeException {
+ run.run();
+ }
+}
diff --git a/src/main/java/de/neemann/digital/gui/sync/Sync.java b/src/main/java/de/neemann/digital/gui/sync/Sync.java
new file mode 100644
index 000000000..f87051b8e
--- /dev/null
+++ b/src/main/java/de/neemann/digital/gui/sync/Sync.java
@@ -0,0 +1,34 @@
+package de.neemann.digital.gui.sync;
+
+import de.neemann.digital.core.NodeException;
+
+/**
+ * Simple sync interface
+ *
+ * @author hneemann
+ */
+public interface Sync {
+
+ /**
+ * Calls the given runnable
+ *
+ * @param run the runnable to execute
+ */
+ void access(Runnable run);
+
+ /**
+ * Same as access, but catches an exception
+ *
+ * @param run the runnable to execute
+ * @throws NodeException NodeException
+ */
+ void accessNEx(ModelRun run) throws NodeException;
+
+ /**
+ * Like runnable but throws an exception
+ */
+ interface ModelRun {
+ void run() throws NodeException;
+ }
+
+}
diff --git a/src/main/java/de/neemann/digital/gui/sync/package-info.java b/src/main/java/de/neemann/digital/gui/sync/package-info.java
new file mode 100644
index 000000000..6c4a3d1a9
--- /dev/null
+++ b/src/main/java/de/neemann/digital/gui/sync/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * Classes to allow a simple synchronisation of model access.
+ * The problem is, that every modification of a {@link de.neemann.digital.core.ObservableValue}
+ * is a model access and needs to be synchronized. Synchronisation is necessary only if
+ * the runtime clock is running activated. If the runtime clock does not run, all modifications
+ * on the model are done by the GUI thread.
+ *
+ * @author hneemann
+ */
+package de.neemann.digital.gui.sync;