From 90849a2efcce44044845719d7bdf0c4cefb86874 Mon Sep 17 00:00:00 2001 From: hneemann Date: Sat, 9 Jul 2016 15:22:16 +0200 Subject: [PATCH 1/5] new sync model --- src/main/dig/processor/Graphics.dig | 44 +- src/main/dig/processor/Processor.dig | 527 +++++++++++------- .../de/neemann/digital/core/element/Keys.java | 2 +- .../digital/draw/elements/VisualElement.java | 13 +- .../digital/draw/model/RealTimeClock.java | 95 +++- .../digital/draw/shapes/ButtonShape.java | 19 +- .../digital/draw/shapes/ClockShape.java | 8 +- .../digital/draw/shapes/DataShape.java | 3 +- .../digital/draw/shapes/InputShape.java | 19 +- .../digital/draw/shapes/Interactor.java | 5 +- .../draw/shapes/InteractorInterface.java | 7 +- .../neemann/digital/draw/shapes/RAMShape.java | 5 +- .../neemann/digital/gui/GuiModelObserver.java | 6 +- .../java/de/neemann/digital/gui/Main.java | 29 +- .../gui/components/CircuitComponent.java | 12 +- .../digital/gui/components/DataEditor.java | 17 +- .../digital/gui/components/EditorFactory.java | 5 +- .../digital/gui/components/ProbeDialog.java | 18 +- .../gui/components/SingleValueDialog.java | 11 +- .../digital/gui/components/data/DataSet.java | 4 +- .../gui/components/data/DataSetDialog.java | 23 +- .../de/neemann/digital/gui/sync/LockSync.java | 36 ++ .../de/neemann/digital/gui/sync/NoSync.java | 23 + .../de/neemann/digital/gui/sync/Sync.java | 18 + 24 files changed, 654 insertions(+), 295 deletions(-) create mode 100644 src/main/java/de/neemann/digital/gui/sync/LockSync.java create mode 100644 src/main/java/de/neemann/digital/gui/sync/NoSync.java create mode 100644 src/main/java/de/neemann/digital/gui/sync/Sync.java 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..ec5c27599 100644 --- a/src/main/dig/processor/Processor.dig +++ b/src/main/dig/processor/Processor.dig @@ -155,26 +155,183 @@ Data - 13 - 5137 + 166 5120 - 6145 - 15888 - 29521 - 15375 - 26107 - 8193 - 16400 - 29521 - 15360 - 26107 - 28661 + 5137 + 32768 + 4960 + 33168 + 4944 + 5251 + 5408 + 5179 + 8417 + 5373 + 19695 + 28305 + 6177 + 8417 + 5618 + 19695 + 28300 + 8225 + 6193 + 8417 + 5624 + 19695 + 28294 + 8225 + 8417 + 32798 + 4848 + 19695 + 28288 + 6177 + 6193 + 8417 + 32805 + 4848 + 19695 + 28281 + 28198 + 5168 + 5152 + 5280 + 8417 + 32814 + 4848 + 19695 + 28195 + 8417 + 32819 + 4848 + 19695 + 28259 + 15488 + 24069 + 15474 + 24069 + 15475 + 24067 + 28163 + 15475 + 25601 + 5283 + 650 + 8417 + 32835 + 4848 + 19695 + 28251 + 6177 + 15652 + 26082 + 6193 + 15668 + 26078 + 12433 + 65535 + 28681 + 582 + 613 + 596 + 31744 + 28629 + 5232 + 8241 + 8417 + 32856 + 4848 + 19695 + 28207 + 6177 + 8417 + 32862 + 4848 + 19695 + 28201 + 6193 + 8417 + 32868 + 4848 + 19695 + 28195 + 6193 + 8417 + 32874 + 4848 + 19695 + 28189 + 8225 + 8417 + 32880 + 4848 + 19695 + 28183 + 8225 + 8417 + 32886 + 4848 + 19695 + 28177 + 8241 + 8417 + 32892 + 4848 + 19695 + 28171 + 8241 + 8417 + 32898 + 4848 + 19695 + 28165 + 6177 + 6193 + 20222 + 6369 + 27151 + 15652 + 25098 + 15668 + 25096 + 579 + 14148 + 1090 + 1093 + 31364 + 15488 + 24065 + 6257 + 20222 + 6369 + 27151 + 579 + 14148 + 1090 + 1093 + 31364 + 20222 + 6369 + 27151 + 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/Conway.hex + + + autoReload + true @@ -191,8 +348,12 @@ Bits 16 + + flipSelPos + true + - + PC.dig @@ -212,7 +373,7 @@ Frequency - 50 + 500000 @@ -225,7 +386,7 @@ - + LED @@ -235,7 +396,7 @@ - + LED @@ -245,7 +406,7 @@ - + LED @@ -255,7 +416,7 @@ - + LED @@ -265,7 +426,7 @@ - + LED @@ -275,7 +436,7 @@ - + LED @@ -285,7 +446,7 @@ - + LED @@ -295,7 +456,7 @@ - + LED @@ -305,7 +466,7 @@ - + LED @@ -315,7 +476,7 @@ - + LED @@ -325,7 +486,7 @@ - + LED @@ -335,7 +496,7 @@ - + LED @@ -345,7 +506,7 @@ - + LED @@ -355,7 +516,7 @@ - + LED @@ -365,7 +526,7 @@ - + LED @@ -375,7 +536,7 @@ - + LED @@ -385,7 +546,7 @@ - + Driver @@ -644,14 +805,14 @@ rotation - + NetName stPC - + Tunnel @@ -817,7 +978,7 @@ ioW - + Tunnel @@ -883,7 +1044,7 @@ C - + Tunnel @@ -912,7 +1073,7 @@ /home/hneemann/Dokumente/Java/digital/src/main/dig/processor/Terminal.dig - + Tunnel @@ -926,7 +1087,7 @@ C - + Tunnel @@ -940,12 +1101,12 @@ ioW - + /home/hneemann/Dokumente/Java/digital/src/main/dig/processor/Graphics.dig - + Tunnel @@ -959,7 +1120,7 @@ ioW - + Tunnel @@ -973,7 +1134,7 @@ ioR - + Tunnel @@ -987,69 +1148,73 @@ C - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + @@ -1059,10 +1224,6 @@ - - - - @@ -1075,6 +1236,22 @@ + + + + + + + + + + + + + + + + @@ -1111,18 +1288,6 @@ - - - - - - - - - - - - @@ -1131,6 +1296,10 @@ + + + + @@ -1140,8 +1309,20 @@ - - + + + + + + + + + + + + + + @@ -1168,16 +1349,16 @@ - - + + - - + + - - + + @@ -1193,7 +1374,7 @@ - + @@ -1221,32 +1402,12 @@ - + - + - - - - - - - - - - - - - - - - - - - - @@ -1259,6 +1420,18 @@ + + + + + + + + + + + + @@ -1268,8 +1441,8 @@ - - + + @@ -1279,10 +1452,6 @@ - - - - @@ -1304,20 +1473,8 @@ - - - - - - - - - - - - - - + + @@ -1328,7 +1485,15 @@ - + + + + + + + + + @@ -1337,7 +1502,7 @@ - + @@ -1359,10 +1524,6 @@ - - - - @@ -1372,12 +1533,16 @@ - - + + - - + + + + + + @@ -1407,13 +1572,17 @@ + + + + - - + + @@ -1428,16 +1597,12 @@ - - + + - - - - - + @@ -1452,20 +1617,20 @@ - - - - - - + + - - + + + + + + @@ -1479,10 +1644,6 @@ - - - - @@ -1497,7 +1658,11 @@ - + + + + + @@ -1535,18 +1700,6 @@ - - - - - - - - - - - - @@ -1556,8 +1709,8 @@ - - + + 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/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..3005f28a5 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,26 @@ 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.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 int frequency; private final ObservableValue output; - private ScheduledFuture timer; + private Runner runner; /** * Creates a new real time clock @@ -30,10 +32,11 @@ public class RealTimeClock implements ModelStateObserver { * @param clock the clock element which is modify * @param executor the executor used to schedule the update */ - public RealTimeClock(Model model, Clock clock, ScheduledThreadPoolExecutor executor, ErrorStopper stopper) { + public RealTimeClock(Model model, Clock clock, ScheduledThreadPoolExecutor executor, ErrorStopper stopper, Sync modelSync) { this.model = model; this.executor = executor; this.stopper = stopper; + this.modelSync = modelSync; int f = clock.getFrequency(); if (f < 1) f = 1; this.frequency = f; @@ -44,27 +47,89 @@ 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 + if (frequency > 50) // if frequency is high it is not necessary to update the GUI at every clock change 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"); + try { + while (!interrupted()) { + modelSync.accessNEx(() -> { + output.setValue(1 - output.getValue()); + model.doStep(); + }); + } + } catch (NodeException e1) { + stopper.showErrorAndStopModel(Lang.get("msg_clockError"), e1); + } + 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..02a425382 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. @@ -38,7 +40,9 @@ public class GuiModelObserver implements Observer, ModelStateObserver { @Override public void handleEvent(ModelEvent event) { if (changed && event == type) { - component.paintImmediately(0, 0, component.getWidth(), component.getHeight()); + SwingUtilities.invokeLater(() -> { + 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..048846688 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.*; @@ -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)); 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)) @@ -840,8 +851,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/components/CircuitComponent.java b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java index 291d8cbc4..7e2688e58 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,7 +251,8 @@ 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 @@ -372,7 +376,7 @@ public class CircuitComponent extends JComponent { public void setCircuit(Circuit circuit) { this.circuit = circuit; fitCircuit(); - setModeAndReset(false); + setModeAndReset(false, NoSync.INST); } /** @@ -982,7 +986,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 +1020,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/sync/LockSync.java b/src/main/java/de/neemann/digital/gui/sync/LockSync.java new file mode 100644 index 000000000..3ef1be023 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/sync/LockSync.java @@ -0,0 +1,36 @@ +package de.neemann.digital.gui.sync; + +import de.neemann.digital.core.NodeException; + +import java.util.concurrent.locks.ReentrantLock; + +/** + * @author hneemann + */ +public class LockSync implements Sync { + private final ReentrantLock lock; + + 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..c0cefe0f6 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/sync/NoSync.java @@ -0,0 +1,23 @@ +package de.neemann.digital.gui.sync; + +import de.neemann.digital.core.NodeException; + +/** + * @author hneemann + */ +public final class NoSync implements Sync { + 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..8c7d3b175 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/sync/Sync.java @@ -0,0 +1,18 @@ +package de.neemann.digital.gui.sync; + +import de.neemann.digital.core.NodeException; + +/** + * @author hneemann + */ +public interface Sync { + + void access(Runnable run); + + void accessNEx(ModelRun run) throws NodeException; + + interface ModelRun { + void run() throws NodeException; + } + +} From 377674f7c4665288e578f440b3de35a4ce5a4a1a Mon Sep 17 00:00:00 2001 From: hneemann Date: Sat, 9 Jul 2016 18:20:21 +0200 Subject: [PATCH 2/5] adds some documentation --- src/main/dig/processor/Processor.dig | 320 ++++++++---------- .../de/neemann/digital/gui/sync/LockSync.java | 7 +- .../de/neemann/digital/gui/sync/NoSync.java | 6 + .../de/neemann/digital/gui/sync/Sync.java | 16 + .../digital/gui/sync/package-info.java | 10 + 5 files changed, 176 insertions(+), 183 deletions(-) create mode 100644 src/main/java/de/neemann/digital/gui/sync/package-info.java diff --git a/src/main/dig/processor/Processor.dig b/src/main/dig/processor/Processor.dig index ec5c27599..d56db3d74 100644 --- a/src/main/dig/processor/Processor.dig +++ b/src/main/dig/processor/Processor.dig @@ -11,7 +11,7 @@ - + Register.dig @@ -155,58 +155,52 @@ Data - 166 - 5120 - 5137 + 126 32768 4960 33168 4944 + 5264 5251 5408 5179 8417 - 5373 + 5372 19695 - 28305 + 28266 6177 8417 - 5618 + 5617 19695 - 28300 + 28261 8225 6193 8417 - 5624 + 5623 19695 - 28294 + 28255 8225 8417 - 32798 + 32797 4848 19695 - 28288 + 28249 6177 6193 8417 - 32805 + 32804 4848 19695 - 28281 - 28198 + 28242 + 28190 + 517 + 534 5168 5152 5280 - 8417 - 32814 - 4848 - 19695 - 28195 - 8417 - 32819 - 4848 - 19695 - 28259 + 32840 + 26816 + 31360 15488 24069 15474 @@ -217,18 +211,15 @@ 15475 25601 5283 - 650 - 8417 - 32835 - 4848 - 19695 - 28251 + 29722 + 6145 + 6161 6177 15652 - 26082 + 26092 6193 15668 - 26078 + 26088 12433 65535 28681 @@ -236,84 +227,53 @@ 613 596 31744 - 28629 + 28637 5232 8241 - 8417 - 32856 - 4848 - 19695 - 28207 + 8452 + 32877 + 26800 6177 - 8417 - 32862 - 4848 - 19695 - 28201 + 6145 + 32877 + 26800 6193 - 8417 - 32868 - 4848 - 19695 - 28195 + 6404 + 32877 + 26800 6193 - 8417 - 32874 - 4848 - 19695 - 28189 + 6404 + 32877 + 26800 8225 - 8417 - 32880 - 4848 - 19695 - 28183 + 8193 + 32877 + 26800 8225 - 8417 - 32886 - 4848 - 19695 - 28177 + 8193 + 32877 + 26800 8241 - 8417 - 32892 - 4848 - 19695 - 28171 + 8452 + 32877 + 26800 8241 - 8417 - 32898 - 4848 - 19695 - 28165 + 8452 + 32877 + 26800 6177 6193 - 20222 - 6369 - 27151 + 6405 + 27148 15652 - 25098 + 25094 15668 - 25096 - 579 - 14148 - 1090 - 1093 - 31364 + 25092 + 31360 15488 24065 6257 - 20222 - 6369 - 27151 - 579 - 14148 - 1090 - 1093 - 31364 - 20222 - 6369 - 27151 + 27147 579 14148 1090 @@ -327,11 +287,7 @@ lastDataFile - /home/hneemann/Dokumente/Java/assembler/assembler3/src/main/asm/Conway.hex - - - autoReload - true + /home/hneemann/Dokumente/Java/assembler/assembler3/src/main/asm/Conway2.hex @@ -396,7 +352,7 @@ - + LED @@ -406,7 +362,7 @@ - + LED @@ -416,7 +372,7 @@ - + LED @@ -426,7 +382,7 @@ - + LED @@ -436,7 +392,7 @@ - + LED @@ -446,7 +402,7 @@ - + LED @@ -456,7 +412,7 @@ - + LED @@ -466,7 +422,7 @@ - + LED @@ -476,7 +432,7 @@ - + LED @@ -486,7 +442,7 @@ - + LED @@ -496,7 +452,7 @@ - + LED @@ -506,7 +462,7 @@ - + LED @@ -516,7 +472,7 @@ - + LED @@ -526,7 +482,7 @@ - + LED @@ -536,7 +492,7 @@ - + LED @@ -546,7 +502,7 @@ - + Driver @@ -602,7 +558,7 @@ stPC - + Tunnel @@ -616,7 +572,7 @@ WE - + Tunnel @@ -630,7 +586,7 @@ imm - + Tunnel @@ -644,7 +600,7 @@ abs - + Tunnel @@ -658,7 +614,7 @@ muxB - + Tunnel @@ -672,7 +628,7 @@ src2D - + Tunnel @@ -686,7 +642,7 @@ muxA - + Tunnel @@ -700,7 +656,7 @@ ALU2D - + Tunnel @@ -714,7 +670,7 @@ ALUop - + Tunnel @@ -728,7 +684,7 @@ sf - + Tunnel @@ -742,7 +698,7 @@ br - + Tunnel @@ -756,7 +712,7 @@ st - + Tunnel @@ -770,7 +726,7 @@ ld - + Tunnel @@ -784,7 +740,7 @@ ioW - + Tunnel @@ -798,7 +754,7 @@ ioR - + Tunnel @@ -1153,64 +1109,64 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + @@ -1549,8 +1505,8 @@ - - + + @@ -1573,28 +1529,24 @@ - - - - - - + + - - + + - - + + @@ -1605,8 +1557,8 @@ - - + + @@ -1625,16 +1577,20 @@ - - + + - - + + + + + + @@ -1665,8 +1621,8 @@ - - + + @@ -1689,8 +1645,8 @@ - - + + @@ -1705,8 +1661,8 @@ - - + + @@ -1714,35 +1670,35 @@ - + - - + + - - + + - - + + - - + + - - + + diff --git a/src/main/java/de/neemann/digital/gui/sync/LockSync.java b/src/main/java/de/neemann/digital/gui/sync/LockSync.java index 3ef1be023..b624d6c18 100644 --- a/src/main/java/de/neemann/digital/gui/sync/LockSync.java +++ b/src/main/java/de/neemann/digital/gui/sync/LockSync.java @@ -5,17 +5,22 @@ 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){ + public void access(Runnable run) { lock.lock(); try { run.run(); diff --git a/src/main/java/de/neemann/digital/gui/sync/NoSync.java b/src/main/java/de/neemann/digital/gui/sync/NoSync.java index c0cefe0f6..545d17bde 100644 --- a/src/main/java/de/neemann/digital/gui/sync/NoSync.java +++ b/src/main/java/de/neemann/digital/gui/sync/NoSync.java @@ -3,9 +3,15 @@ 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() { diff --git a/src/main/java/de/neemann/digital/gui/sync/Sync.java b/src/main/java/de/neemann/digital/gui/sync/Sync.java index 8c7d3b175..f87051b8e 100644 --- a/src/main/java/de/neemann/digital/gui/sync/Sync.java +++ b/src/main/java/de/neemann/digital/gui/sync/Sync.java @@ -3,14 +3,30 @@ 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; From 7d22c4c2731485c60a4385a3d3b7f683f12b31e7 Mon Sep 17 00:00:00 2001 From: hneemann Date: Sat, 9 Jul 2016 22:15:37 +0200 Subject: [PATCH 3/5] fixed bug concerning speed testing --- README.md | 4 ++-- README_de.md | 4 ++-- src/main/java/de/neemann/digital/core/SpeedTest.java | 1 + .../de/neemann/digital/draw/model/RealTimeClock.java | 7 +++++++ .../java/de/neemann/digital/gui/GuiModelObserver.java | 11 ++++++++--- .../digital/gui/components/CircuitComponent.java | 5 +++-- 6 files changed, 23 insertions(+), 9 deletions(-) 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/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/draw/model/RealTimeClock.java b/src/main/java/de/neemann/digital/draw/model/RealTimeClock.java index 3005f28a5..0aa7fe3a8 100644 --- a/src/main/java/de/neemann/digital/draw/model/RealTimeClock.java +++ b/src/main/java/de/neemann/digital/draw/model/RealTimeClock.java @@ -110,16 +110,23 @@ public class RealTimeClock implements ModelStateObserver { @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; + + System.out.println(counter/time/2+"kHz"); + System.out.println("thread end"); } }; diff --git a/src/main/java/de/neemann/digital/gui/GuiModelObserver.java b/src/main/java/de/neemann/digital/gui/GuiModelObserver.java index 02a425382..b6d6dbf85 100644 --- a/src/main/java/de/neemann/digital/gui/GuiModelObserver.java +++ b/src/main/java/de/neemann/digital/gui/GuiModelObserver.java @@ -20,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. @@ -40,9 +41,13 @@ public class GuiModelObserver implements Observer, ModelStateObserver { @Override public void handleEvent(ModelEvent event) { if (changed && event == type) { - SwingUtilities.invokeLater(() -> { - component.paintImmediately(0, 0, component.getWidth(), component.getHeight()); - }); + if (!paintPending) { + paintPending = true; + SwingUtilities.invokeLater(() -> { + component.paintImmediately(0, 0, component.getWidth(), component.getHeight()); + paintPending = false; + }); + } changed = false; } } 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 7e2688e58..fe7d1ad19 100644 --- a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java +++ b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java @@ -255,10 +255,11 @@ public class CircuitComponent extends JComponent { this.modelSync = modelSync; if (runMode) mouseRun.activate(); - else + else { mouseNormal.activate(); + circuit.clearState(); + } requestFocusInWindow(); - circuit.clearState(); } /** From 5f9cd232f8bac8b888fa6b36310ba3ecc5694901 Mon Sep 17 00:00:00 2001 From: hneemann Date: Sat, 9 Jul 2016 22:47:58 +0200 Subject: [PATCH 4/5] more conservative painting --- src/main/java/de/neemann/digital/gui/GuiModelObserver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/neemann/digital/gui/GuiModelObserver.java b/src/main/java/de/neemann/digital/gui/GuiModelObserver.java index b6d6dbf85..3cefd5997 100644 --- a/src/main/java/de/neemann/digital/gui/GuiModelObserver.java +++ b/src/main/java/de/neemann/digital/gui/GuiModelObserver.java @@ -44,8 +44,8 @@ public class GuiModelObserver implements Observer, ModelStateObserver { if (!paintPending) { paintPending = true; SwingUtilities.invokeLater(() -> { - component.paintImmediately(0, 0, component.getWidth(), component.getHeight()); paintPending = false; + component.paintImmediately(0, 0, component.getWidth(), component.getHeight()); }); } changed = false; From 4a0ee2e5133bc61f0d9eca8bc3f79c31177f3fa9 Mon Sep 17 00:00:00 2001 From: hneemann Date: Sun, 10 Jul 2016 10:23:08 +0200 Subject: [PATCH 5/5] some miner cleanups according synchronisation --- .../digital/analyse/TruthTableTableModel.java | 2 +- .../digital/core/memory/DataField.java | 26 ++++++++----------- .../digital/draw/model/RealTimeClock.java | 16 +++++++----- .../java/de/neemann/digital/gui/Main.java | 19 +++++++++----- .../neemann/digital/gui/StatusInterface.java | 15 +++++++++++ .../gui/components/graphics/GraphicCard.java | 4 +++ .../components/listing/ROMListingDialog.java | 1 - 7 files changed, 53 insertions(+), 30 deletions(-) create mode 100644 src/main/java/de/neemann/digital/gui/StatusInterface.java 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/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/model/RealTimeClock.java b/src/main/java/de/neemann/digital/draw/model/RealTimeClock.java index 0aa7fe3a8..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,6 +4,7 @@ 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; @@ -21,6 +22,7 @@ public class RealTimeClock implements ModelStateObserver { 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 Runner runner; @@ -31,12 +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, Sync modelSync) { + 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; @@ -48,7 +52,7 @@ public class RealTimeClock implements ModelStateObserver { switch (event) { case STARTED: if (frequency > 50) // if frequency is high it is not necessary to update the GUI at every clock change - output.removeObserver(GuiModelObserver.class); + modelSync.access(() -> output.removeObserver(GuiModelObserver.class)); int delay = 500000 / frequency; if (delay < 10) @@ -110,8 +114,8 @@ public class RealTimeClock implements ModelStateObserver { @Override public void run() { System.out.println("thread start"); - long time= System.currentTimeMillis(); - long counter=0; + long time = System.currentTimeMillis(); + long counter = 0; try { while (!interrupted()) { modelSync.accessNEx(() -> { @@ -123,9 +127,9 @@ public class RealTimeClock implements ModelStateObserver { } catch (NodeException e1) { stopper.showErrorAndStopModel(Lang.get("msg_clockError"), e1); } - time=System.currentTimeMillis()-time; + time = System.currentTimeMillis() - time; - System.out.println(counter/time/2+"kHz"); + status.setStatus(counter / time / 2 + "kHz"); System.out.println("thread end"); } diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java index 048846688..c1931d4c1 100644 --- a/src/main/java/de/neemann/digital/gui/Main.java +++ b/src/main/java/de/neemann/digital/gui/Main.java @@ -59,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; @@ -678,17 +678,17 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E statusLabel.setText(Lang.get("msg_N_nodes", model.size())); realtimeClockRunning = false; - modelSync=null; + modelSync = null; if (globalRunClock) for (Clock c : model.getClocks()) if (c.getFrequency() > 0) { - if (modelSync==null) - modelSync=new LockSync(); - model.addObserver(new RealTimeClock(model, c, timerExecuter, this, modelSync)); + if (modelSync == null) + modelSync = new LockSync(); + model.addObserver(new RealTimeClock(model, c, timerExecuter, this, modelSync, this)); realtimeClockRunning = true; } - if (modelSync==null) - modelSync= NoSync.INST; + if (modelSync == null) + modelSync = NoSync.INST; circuitComponent.setModeAndReset(true, modelSync); @@ -841,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; 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/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; } }