mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-13 06:49:36 -04:00
integrated sync branch
This commit is contained in:
commit
07b730a87b
@ -31,7 +31,7 @@ This are the main features of Digital:
|
|||||||
- You can test your design by creating test cases and execute them.
|
- 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.
|
- 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.
|
- 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.
|
- 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.
|
- 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))
|
- 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
|
If a complete processors is simulated, it is possible to calculate the simulation without an update of the
|
||||||
graphical representation.
|
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.
|
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.
|
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
|
In this way, an assembler instruction BRK can be implemented, which then can be used to insert break points
|
||||||
|
@ -26,7 +26,7 @@ Folgende Features zeichnen Digital aus:
|
|||||||
- Analyse und Synthese von kombinatorischen Schaltungen und Schaltwerken.
|
- Analyse und Synthese von kombinatorischen Schaltungen und Schaltwerken.
|
||||||
- Viele Beispiele: Vom Transmision-Gate D-FF bis zum kompletten MIPS-ähnlichem Prozessor.
|
- 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.
|
- 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.
|
- 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.
|
- 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))
|
- 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 ###
|
### Performance ###
|
||||||
|
|
||||||
Werden komplette Prozessoren simuliert, ist es möglich die Simulation zu berechnen ohne die grafische Anzeige zu aktualisieren.
|
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.
|
komplexere Übungen ausreichend ist.
|
||||||
Es gibt ein Break-Gatter welches einen einzelnen Eingang hat. Wechselt dieser Eingang von low auf high wird dieser
|
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
|
schnelle Lauf beendet. Auf diese Weise lässt sich eine Assembler-Anweisung BRK implementieren, womit sich dann Break-Points
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
<int>16</int>
|
<int>16</int>
|
||||||
</entry>
|
</entry>
|
||||||
</elementAttributes>
|
</elementAttributes>
|
||||||
<pos x="180" y="220"/>
|
<pos x="200" y="220"/>
|
||||||
</visualElement>
|
</visualElement>
|
||||||
<visualElement>
|
<visualElement>
|
||||||
<elementName>In</elementName>
|
<elementName>In</elementName>
|
||||||
@ -24,7 +24,7 @@
|
|||||||
<string>R</string>
|
<string>R</string>
|
||||||
</entry>
|
</entry>
|
||||||
</elementAttributes>
|
</elementAttributes>
|
||||||
<pos x="180" y="460"/>
|
<pos x="200" y="460"/>
|
||||||
</visualElement>
|
</visualElement>
|
||||||
<visualElement>
|
<visualElement>
|
||||||
<elementName>Comparator</elementName>
|
<elementName>Comparator</elementName>
|
||||||
@ -101,7 +101,7 @@
|
|||||||
<string>C</string>
|
<string>C</string>
|
||||||
</entry>
|
</entry>
|
||||||
</elementAttributes>
|
</elementAttributes>
|
||||||
<pos x="180" y="540"/>
|
<pos x="200" y="540"/>
|
||||||
</visualElement>
|
</visualElement>
|
||||||
<visualElement>
|
<visualElement>
|
||||||
<elementName>And</elementName>
|
<elementName>And</elementName>
|
||||||
@ -160,6 +160,10 @@
|
|||||||
<visualElement>
|
<visualElement>
|
||||||
<elementName>D_FF</elementName>
|
<elementName>D_FF</elementName>
|
||||||
<elementAttributes>
|
<elementAttributes>
|
||||||
|
<entry>
|
||||||
|
<string>valueIsProbe</string>
|
||||||
|
<boolean>true</boolean>
|
||||||
|
</entry>
|
||||||
<entry>
|
<entry>
|
||||||
<string>Label</string>
|
<string>Label</string>
|
||||||
<string>Bank-FF</string>
|
<string>Bank-FF</string>
|
||||||
@ -175,7 +179,7 @@
|
|||||||
<string>W</string>
|
<string>W</string>
|
||||||
</entry>
|
</entry>
|
||||||
</elementAttributes>
|
</elementAttributes>
|
||||||
<pos x="180" y="380"/>
|
<pos x="200" y="380"/>
|
||||||
</visualElement>
|
</visualElement>
|
||||||
<visualElement>
|
<visualElement>
|
||||||
<elementName>Splitter</elementName>
|
<elementName>Splitter</elementName>
|
||||||
@ -256,7 +260,7 @@
|
|||||||
<p2 x="460" y="520"/>
|
<p2 x="460" y="520"/>
|
||||||
</wire>
|
</wire>
|
||||||
<wire>
|
<wire>
|
||||||
<p1 x="180" y="460"/>
|
<p1 x="200" y="460"/>
|
||||||
<p2 x="580" y="460"/>
|
<p2 x="580" y="460"/>
|
||||||
</wire>
|
</wire>
|
||||||
<wire>
|
<wire>
|
||||||
@ -264,7 +268,7 @@
|
|||||||
<p2 x="460" y="300"/>
|
<p2 x="460" y="300"/>
|
||||||
</wire>
|
</wire>
|
||||||
<wire>
|
<wire>
|
||||||
<p1 x="220" y="300"/>
|
<p1 x="240" y="300"/>
|
||||||
<p2 x="360" y="300"/>
|
<p2 x="360" y="300"/>
|
||||||
</wire>
|
</wire>
|
||||||
<wire>
|
<wire>
|
||||||
@ -292,7 +296,7 @@
|
|||||||
<p2 x="680" y="180"/>
|
<p2 x="680" y="180"/>
|
||||||
</wire>
|
</wire>
|
||||||
<wire>
|
<wire>
|
||||||
<p1 x="220" y="180"/>
|
<p1 x="240" y="180"/>
|
||||||
<p2 x="580" y="180"/>
|
<p2 x="580" y="180"/>
|
||||||
</wire>
|
</wire>
|
||||||
<wire>
|
<wire>
|
||||||
@ -324,11 +328,11 @@
|
|||||||
<p2 x="580" y="220"/>
|
<p2 x="580" y="220"/>
|
||||||
</wire>
|
</wire>
|
||||||
<wire>
|
<wire>
|
||||||
<p1 x="180" y="220"/>
|
<p1 x="200" y="220"/>
|
||||||
<p2 x="220" y="220"/>
|
<p2 x="240" y="220"/>
|
||||||
</wire>
|
</wire>
|
||||||
<wire>
|
<wire>
|
||||||
<p1 x="220" y="220"/>
|
<p1 x="240" y="220"/>
|
||||||
<p2 x="360" y="220"/>
|
<p2 x="360" y="220"/>
|
||||||
</wire>
|
</wire>
|
||||||
<wire>
|
<wire>
|
||||||
@ -336,7 +340,7 @@
|
|||||||
<p2 x="720" y="380"/>
|
<p2 x="720" y="380"/>
|
||||||
</wire>
|
</wire>
|
||||||
<wire>
|
<wire>
|
||||||
<p1 x="180" y="380"/>
|
<p1 x="200" y="380"/>
|
||||||
<p2 x="400" y="380"/>
|
<p2 x="400" y="380"/>
|
||||||
</wire>
|
</wire>
|
||||||
<wire>
|
<wire>
|
||||||
@ -348,7 +352,7 @@
|
|||||||
<p2 x="580" y="540"/>
|
<p2 x="580" y="540"/>
|
||||||
</wire>
|
</wire>
|
||||||
<wire>
|
<wire>
|
||||||
<p1 x="180" y="540"/>
|
<p1 x="200" y="540"/>
|
||||||
<p2 x="340" y="540"/>
|
<p2 x="340" y="540"/>
|
||||||
</wire>
|
</wire>
|
||||||
<wire>
|
<wire>
|
||||||
@ -367,6 +371,14 @@
|
|||||||
<p1 x="400" y="380"/>
|
<p1 x="400" y="380"/>
|
||||||
<p2 x="400" y="560"/>
|
<p2 x="400" y="560"/>
|
||||||
</wire>
|
</wire>
|
||||||
|
<wire>
|
||||||
|
<p1 x="240" y="180"/>
|
||||||
|
<p2 x="240" y="220"/>
|
||||||
|
</wire>
|
||||||
|
<wire>
|
||||||
|
<p1 x="240" y="220"/>
|
||||||
|
<p2 x="240" y="300"/>
|
||||||
|
</wire>
|
||||||
<wire>
|
<wire>
|
||||||
<p1 x="820" y="400"/>
|
<p1 x="820" y="400"/>
|
||||||
<p2 x="820" y="600"/>
|
<p2 x="820" y="600"/>
|
||||||
@ -395,14 +407,6 @@
|
|||||||
<p1 x="680" y="180"/>
|
<p1 x="680" y="180"/>
|
||||||
<p2 x="680" y="360"/>
|
<p2 x="680" y="360"/>
|
||||||
</wire>
|
</wire>
|
||||||
<wire>
|
|
||||||
<p1 x="220" y="180"/>
|
|
||||||
<p2 x="220" y="220"/>
|
|
||||||
</wire>
|
|
||||||
<wire>
|
|
||||||
<p1 x="220" y="220"/>
|
|
||||||
<p2 x="220" y="300"/>
|
|
||||||
</wire>
|
|
||||||
<wire>
|
<wire>
|
||||||
<p1 x="540" y="520"/>
|
<p1 x="540" y="520"/>
|
||||||
<p2 x="540" y="600"/>
|
<p2 x="540" y="600"/>
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,7 @@ public class TruthTableTableModel implements TableModel {
|
|||||||
public static final String[] STATENAMES = new String[]{"0", "1", "x"};
|
public static final String[] STATENAMES = new String[]{"0", "1", "x"};
|
||||||
|
|
||||||
private final TruthTable truthTable;
|
private final TruthTable truthTable;
|
||||||
private ArrayList<TableModelListener> listeners = new ArrayList<>();
|
private final ArrayList<TableModelListener> listeners = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance
|
* Creates a new instance
|
||||||
|
@ -49,6 +49,7 @@ public class SpeedTest {
|
|||||||
for (int i = 0; i < LOOPCOUNTER; i++) {
|
for (int i = 0; i < LOOPCOUNTER; i++) {
|
||||||
state = 1 - state;
|
state = 1 - state;
|
||||||
clockValue.setValue(state);
|
clockValue.setValue(state);
|
||||||
|
model.doStep();
|
||||||
}
|
}
|
||||||
loops++;
|
loops++;
|
||||||
aktTime = System.currentTimeMillis();
|
aktTime = System.currentTimeMillis();
|
||||||
|
@ -77,7 +77,7 @@ public final class Keys {
|
|||||||
*/
|
*/
|
||||||
public static final Key<Integer> FREQUENCY
|
public static final Key<Integer> FREQUENCY
|
||||||
= new Key.KeyInteger("Frequency", 1)
|
= 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);
|
.setMin(1);
|
||||||
/**
|
/**
|
||||||
* the bit count of a muxer or decoder
|
* the bit count of a muxer or decoder
|
||||||
|
@ -20,7 +20,7 @@ public class DataField {
|
|||||||
private long[] data;
|
private long[] data;
|
||||||
private final int bits;
|
private final int bits;
|
||||||
|
|
||||||
private transient ArrayList<DataListener> listeners;
|
private final transient ArrayList<DataListener> listeners = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new DataField
|
* Creates a new DataField
|
||||||
@ -158,9 +158,9 @@ public class DataField {
|
|||||||
* @param l the listener
|
* @param l the listener
|
||||||
*/
|
*/
|
||||||
public void addListener(DataListener l) {
|
public void addListener(DataListener l) {
|
||||||
if (listeners == null)
|
synchronized (listeners) {
|
||||||
listeners = new ArrayList<>();
|
listeners.add(l);
|
||||||
listeners.add(l);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -169,12 +169,9 @@ public class DataField {
|
|||||||
* @param l the listener to remove
|
* @param l the listener to remove
|
||||||
*/
|
*/
|
||||||
public void removeListener(DataListener l) {
|
public void removeListener(DataListener l) {
|
||||||
if (listeners == null)
|
synchronized (listeners) {
|
||||||
return;
|
listeners.remove(l);
|
||||||
|
}
|
||||||
listeners.remove(l);
|
|
||||||
if (listeners.isEmpty())
|
|
||||||
listeners = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -183,11 +180,10 @@ public class DataField {
|
|||||||
* @param addr the address which value has changed
|
* @param addr the address which value has changed
|
||||||
*/
|
*/
|
||||||
public void fireChanged(int addr) {
|
public void fireChanged(int addr) {
|
||||||
if (listeners == null)
|
synchronized (listeners) {
|
||||||
return;
|
for (DataListener l : listeners)
|
||||||
|
l.valueChanged(addr);
|
||||||
for (DataListener l : listeners)
|
}
|
||||||
l.valueChanged(addr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -6,6 +6,7 @@ import de.neemann.digital.draw.graphics.*;
|
|||||||
import de.neemann.digital.draw.shapes.*;
|
import de.neemann.digital.draw.shapes.*;
|
||||||
import de.neemann.digital.draw.shapes.Shape;
|
import de.neemann.digital.draw.shapes.Shape;
|
||||||
import de.neemann.digital.gui.components.CircuitComponent;
|
import de.neemann.digital.gui.components.CircuitComponent;
|
||||||
|
import de.neemann.digital.gui.sync.Sync;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
@ -261,9 +262,9 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
|
|||||||
* @param pos the position
|
* @param pos the position
|
||||||
* @return true if model is changed
|
* @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)
|
if (interactor != null)
|
||||||
return interactor.clicked(cc, pos, ioState, element);
|
return interactor.clicked(cc, pos, ioState, element, modelSync);
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -276,9 +277,9 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
|
|||||||
* @param pos the position
|
* @param pos the position
|
||||||
* @return true if model is changed
|
* @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)
|
if (interactor != null)
|
||||||
return interactor.pressed(cc, pos, ioState, element);
|
return interactor.pressed(cc, pos, ioState, element, modelSync);
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -291,9 +292,9 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
|
|||||||
* @param pos the position
|
* @param pos the position
|
||||||
* @return true if model is changed
|
* @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)
|
if (interactor != null)
|
||||||
return interactor.released(cc, pos, ioState, element);
|
return interactor.released(cc, pos, ioState, element, modelSync);
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -4,24 +4,28 @@ import de.neemann.digital.core.*;
|
|||||||
import de.neemann.digital.core.wiring.Clock;
|
import de.neemann.digital.core.wiring.Clock;
|
||||||
import de.neemann.digital.gui.ErrorStopper;
|
import de.neemann.digital.gui.ErrorStopper;
|
||||||
import de.neemann.digital.gui.GuiModelObserver;
|
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 de.neemann.digital.lang.Lang;
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The real time clock which is used to fire the models clocks with realtime signals
|
* The real time clock which is used to fire the models clocks with realtime signals
|
||||||
|
*
|
||||||
* @author hneemann
|
* @author hneemann
|
||||||
*/
|
*/
|
||||||
public class RealTimeClock implements ModelStateObserver {
|
public class RealTimeClock implements ModelStateObserver {
|
||||||
private final Model model;
|
private final Model model;
|
||||||
private final ScheduledThreadPoolExecutor executor;
|
private final ScheduledThreadPoolExecutor executor;
|
||||||
private final ErrorStopper stopper;
|
private final ErrorStopper stopper;
|
||||||
|
private final Sync modelSync;
|
||||||
|
private final StatusInterface status;
|
||||||
private final int frequency;
|
private final int frequency;
|
||||||
private final ObservableValue output;
|
private final ObservableValue output;
|
||||||
private ScheduledFuture<?> timer;
|
private Runner runner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new real time clock
|
* Creates a new real time clock
|
||||||
@ -29,11 +33,14 @@ public class RealTimeClock implements ModelStateObserver {
|
|||||||
* @param model the model
|
* @param model the model
|
||||||
* @param clock the clock element which is modify
|
* @param clock the clock element which is modify
|
||||||
* @param executor the executor used to schedule the update
|
* @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.model = model;
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
this.stopper = stopper;
|
this.stopper = stopper;
|
||||||
|
this.modelSync = modelSync;
|
||||||
|
this.status = status;
|
||||||
int f = clock.getFrequency();
|
int f = clock.getFrequency();
|
||||||
if (f < 1) f = 1;
|
if (f < 1) f = 1;
|
||||||
this.frequency = f;
|
this.frequency = f;
|
||||||
@ -44,27 +51,96 @@ public class RealTimeClock implements ModelStateObserver {
|
|||||||
public void handleEvent(ModelEvent event) {
|
public void handleEvent(ModelEvent event) {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case STARTED:
|
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);
|
modelSync.access(() -> output.removeObserver(GuiModelObserver.class));
|
||||||
|
|
||||||
int delay = 500 / frequency;
|
int delay = 500000 / frequency;
|
||||||
if (delay < 1) delay = 1;
|
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(() -> {
|
interface Runner {
|
||||||
output.setValue(1 - output.getValue());
|
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 {
|
try {
|
||||||
model.doStep();
|
modelSync.accessNEx(() -> {
|
||||||
|
output.setValue(1 - output.getValue());
|
||||||
|
model.doStep();
|
||||||
|
});
|
||||||
} catch (NodeException e1) {
|
} catch (NodeException e1) {
|
||||||
stopper.showErrorAndStopModel(Lang.get("msg_clockError"), e1);
|
stopper.showErrorAndStopModel(Lang.get("msg_clockError"), e1);
|
||||||
timer.cancel(false);
|
timer.cancel(false);
|
||||||
}
|
}
|
||||||
}), delay, delay, TimeUnit.MILLISECONDS);
|
}
|
||||||
|
}, delay, delay, TimeUnit.MICROSECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
@Override
|
||||||
case STOPPED:
|
public void stop() {
|
||||||
if (timer != null)
|
if (timer != null)
|
||||||
timer.cancel(false);
|
timer.cancel(false);
|
||||||
break;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import de.neemann.digital.draw.elements.Pins;
|
|||||||
import de.neemann.digital.draw.graphics.*;
|
import de.neemann.digital.draw.graphics.*;
|
||||||
import de.neemann.digital.draw.graphics.Polygon;
|
import de.neemann.digital.draw.graphics.Polygon;
|
||||||
import de.neemann.digital.gui.components.CircuitComponent;
|
import de.neemann.digital.gui.components.CircuitComponent;
|
||||||
|
import de.neemann.digital.gui.sync.Sync;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
@ -52,21 +53,25 @@ public class ButtonShape implements Shape {
|
|||||||
ioState.getOutput(0).addObserverToValue(guiObserver);
|
ioState.getOutput(0).addObserverToValue(guiObserver);
|
||||||
return new InteractorInterface() {
|
return new InteractorInterface() {
|
||||||
@Override
|
@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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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) {
|
||||||
ObservableValue value = ioState.getOutput(0);
|
modelSync.access(() -> {
|
||||||
value.setValue(1);
|
ObservableValue value = ioState.getOutput(0);
|
||||||
|
value.setValue(1);
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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) {
|
||||||
ObservableValue value = ioState.getOutput(0);
|
modelSync.access(() -> {
|
||||||
value.setValue(0);
|
ObservableValue value = ioState.getOutput(0);
|
||||||
|
value.setValue(0);
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -11,6 +11,7 @@ import de.neemann.digital.draw.elements.Pins;
|
|||||||
import de.neemann.digital.draw.graphics.*;
|
import de.neemann.digital.draw.graphics.*;
|
||||||
import de.neemann.digital.draw.graphics.Polygon;
|
import de.neemann.digital.draw.graphics.Polygon;
|
||||||
import de.neemann.digital.gui.components.CircuitComponent;
|
import de.neemann.digital.gui.components.CircuitComponent;
|
||||||
|
import de.neemann.digital.gui.sync.Sync;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ import static de.neemann.digital.draw.shapes.OutputShape.SIZE;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The Clock shape
|
* The Clock shape
|
||||||
|
*
|
||||||
* @author hneemann
|
* @author hneemann
|
||||||
*/
|
*/
|
||||||
public class ClockShape implements Shape {
|
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
|
ioState.getOutput(0).addObserverToValue(guiObserver); // necessary to replot wires also if component itself does not depend on state
|
||||||
return new Interactor() {
|
return new Interactor() {
|
||||||
@Override
|
@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);
|
ObservableValue value = ioState.getOutput(0);
|
||||||
if (value.getBits() == 1) {
|
if (value.getBits() == 1) {
|
||||||
value.setValue(1 - value.getValue());
|
modelSync.access(() -> {
|
||||||
|
value.setValue(1 - value.getValue());
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -16,6 +16,7 @@ import de.neemann.digital.gui.components.CircuitComponent;
|
|||||||
import de.neemann.digital.gui.components.OrderMerger;
|
import de.neemann.digital.gui.components.OrderMerger;
|
||||||
import de.neemann.digital.gui.components.data.DataSet;
|
import de.neemann.digital.gui.components.data.DataSet;
|
||||||
import de.neemann.digital.gui.components.data.DataSetObserver;
|
import de.neemann.digital.gui.components.data.DataSetObserver;
|
||||||
|
import de.neemann.digital.gui.sync.Sync;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -52,7 +53,7 @@ public class DataShape implements Shape {
|
|||||||
public Interactor applyStateMonitor(IOState ioState, Observer guiObserver) {
|
public Interactor applyStateMonitor(IOState ioState, Observer guiObserver) {
|
||||||
return new Interactor() {
|
return new Interactor() {
|
||||||
@Override
|
@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();
|
dataSet.clear();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import de.neemann.digital.draw.graphics.*;
|
|||||||
import de.neemann.digital.draw.graphics.Polygon;
|
import de.neemann.digital.draw.graphics.Polygon;
|
||||||
import de.neemann.digital.gui.components.CircuitComponent;
|
import de.neemann.digital.gui.components.CircuitComponent;
|
||||||
import de.neemann.digital.gui.components.SingleValueDialog;
|
import de.neemann.digital.gui.components.SingleValueDialog;
|
||||||
|
import de.neemann.digital.gui.sync.Sync;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
@ -52,17 +53,19 @@ public class InputShape implements Shape {
|
|||||||
ioState.getOutput(0).addObserverToValue(guiObserver);
|
ioState.getOutput(0).addObserverToValue(guiObserver);
|
||||||
return new Interactor() {
|
return new Interactor() {
|
||||||
@Override
|
@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);
|
ObservableValue value = ioState.getOutput(0);
|
||||||
if (value.getBits() == 1) {
|
if (value.getBits() == 1) {
|
||||||
if (value.supportsHighZ()) {
|
modelSync.access(() -> {
|
||||||
if (value.isHighZ()) value.set(0, false);
|
if (value.supportsHighZ()) {
|
||||||
else if (value.getValue() == 0) value.setValue(1);
|
if (value.isHighZ()) value.set(0, false);
|
||||||
else value.set(0, true);
|
else if (value.getValue() == 0) value.setValue(1);
|
||||||
} else
|
else value.set(0, true);
|
||||||
value.setValue(1 - value.getValue());
|
} else
|
||||||
|
value.setValue(1 - value.getValue());
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
SingleValueDialog.editValue(pos, value);
|
SingleValueDialog.editValue(pos, value, modelSync);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package de.neemann.digital.draw.shapes;
|
|||||||
import de.neemann.digital.core.element.Element;
|
import de.neemann.digital.core.element.Element;
|
||||||
import de.neemann.digital.draw.elements.IOState;
|
import de.neemann.digital.draw.elements.IOState;
|
||||||
import de.neemann.digital.gui.components.CircuitComponent;
|
import de.neemann.digital.gui.components.CircuitComponent;
|
||||||
|
import de.neemann.digital.gui.sync.Sync;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
@ -17,12 +18,12 @@ import java.awt.*;
|
|||||||
public abstract class Interactor implements InteractorInterface {
|
public abstract class Interactor implements InteractorInterface {
|
||||||
|
|
||||||
@Override
|
@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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package de.neemann.digital.draw.shapes;
|
|||||||
import de.neemann.digital.core.element.Element;
|
import de.neemann.digital.core.element.Element;
|
||||||
import de.neemann.digital.draw.elements.IOState;
|
import de.neemann.digital.draw.elements.IOState;
|
||||||
import de.neemann.digital.gui.components.CircuitComponent;
|
import de.neemann.digital.gui.components.CircuitComponent;
|
||||||
|
import de.neemann.digital.gui.sync.Sync;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
@ -23,7 +24,7 @@ public interface InteractorInterface {
|
|||||||
* @param ioState the state of the element
|
* @param ioState the state of the element
|
||||||
* @return true if model is changed
|
* @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
|
* Called mouse is pressed on running model
|
||||||
@ -33,7 +34,7 @@ public interface InteractorInterface {
|
|||||||
* @param ioState the state of the element
|
* @param ioState the state of the element
|
||||||
* @return true if model is changed
|
* @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
|
* Called mouse is released on running model
|
||||||
@ -43,5 +44,5 @@ public interface InteractorInterface {
|
|||||||
* @param ioState the state of the element
|
* @param ioState the state of the element
|
||||||
* @return true if model is changed
|
* @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);
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import de.neemann.digital.core.memory.RAMInterface;
|
|||||||
import de.neemann.digital.draw.elements.IOState;
|
import de.neemann.digital.draw.elements.IOState;
|
||||||
import de.neemann.digital.gui.components.CircuitComponent;
|
import de.neemann.digital.gui.components.CircuitComponent;
|
||||||
import de.neemann.digital.gui.components.DataEditor;
|
import de.neemann.digital.gui.components.DataEditor;
|
||||||
|
import de.neemann.digital.gui.sync.Sync;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
@ -32,10 +33,10 @@ public class RAMShape extends GenericShape {
|
|||||||
public Interactor applyStateMonitor(IOState ioState, Observer guiObserver) {
|
public Interactor applyStateMonitor(IOState ioState, Observer guiObserver) {
|
||||||
return new Interactor() {
|
return new Interactor() {
|
||||||
@Override
|
@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) {
|
if (element instanceof RAMInterface) {
|
||||||
DataField dataField = ((RAMInterface) element).getMemory();
|
DataField dataField = ((RAMInterface) element).getMemory();
|
||||||
new DataEditor(cc, dataField).showDialog();
|
new DataEditor(cc, dataField, modelSync).showDialog();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ import de.neemann.digital.core.ModelStateObserver;
|
|||||||
import de.neemann.digital.core.Observer;
|
import de.neemann.digital.core.Observer;
|
||||||
import de.neemann.digital.gui.components.CircuitComponent;
|
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 is added to the model if real time timers are started.
|
||||||
* This observer paints the CircuitComponent after a step is calculated.
|
* 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 CircuitComponent component;
|
||||||
private final ModelEvent type;
|
private final ModelEvent type;
|
||||||
private boolean changed = false;
|
private boolean changed = false;
|
||||||
|
private volatile boolean paintPending;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
@ -38,7 +41,13 @@ public class GuiModelObserver implements Observer, ModelStateObserver {
|
|||||||
@Override
|
@Override
|
||||||
public void handleEvent(ModelEvent event) {
|
public void handleEvent(ModelEvent event) {
|
||||||
if (changed && event == type) {
|
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;
|
changed = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,9 @@ import de.neemann.digital.gui.remote.DigitalHandler;
|
|||||||
import de.neemann.digital.gui.remote.RemoteSever;
|
import de.neemann.digital.gui.remote.RemoteSever;
|
||||||
import de.neemann.digital.gui.state.State;
|
import de.neemann.digital.gui.state.State;
|
||||||
import de.neemann.digital.gui.state.StateManager;
|
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.digital.lang.Lang;
|
||||||
import de.neemann.gui.*;
|
import de.neemann.gui.*;
|
||||||
|
|
||||||
@ -56,7 +59,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
|
|||||||
*
|
*
|
||||||
* @author hneemann
|
* @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<Key> ATTR_LIST = new ArrayList<>();
|
private static final ArrayList<Key> ATTR_LIST = new ArrayList<>();
|
||||||
private static boolean experimental;
|
private static boolean experimental;
|
||||||
|
|
||||||
@ -106,7 +109,9 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
|
|||||||
private File filename;
|
private File filename;
|
||||||
private FileHistory fileHistory;
|
private FileHistory fileHistory;
|
||||||
|
|
||||||
|
private Sync modelSync;
|
||||||
private Model model;
|
private Model model;
|
||||||
|
|
||||||
private ModelCreator modelCreator;
|
private ModelCreator modelCreator;
|
||||||
private boolean realtimeClockRunning;
|
private boolean realtimeClockRunning;
|
||||||
|
|
||||||
@ -615,7 +620,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
|
|||||||
public void enter() {
|
public void enter() {
|
||||||
super.enter();
|
super.enter();
|
||||||
clearModelDescription();
|
clearModelDescription();
|
||||||
circuitComponent.setModeAndReset(false);
|
circuitComponent.setModeAndReset(false, NoSync.INST);
|
||||||
doStep.setEnabled(false);
|
doStep.setEnabled(false);
|
||||||
runToBreakAction.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) {
|
private boolean createAndStartModel(boolean globalRunClock, ModelEvent updateEvent) {
|
||||||
try {
|
try {
|
||||||
circuitComponent.removeHighLighted();
|
circuitComponent.removeHighLighted();
|
||||||
circuitComponent.setModeAndReset(true);
|
|
||||||
|
|
||||||
modelCreator = new ModelCreator(circuitComponent.getCircuit(), library);
|
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()));
|
statusLabel.setText(Lang.get("msg_N_nodes", model.size()));
|
||||||
|
|
||||||
realtimeClockRunning = false;
|
realtimeClockRunning = false;
|
||||||
|
modelSync = null;
|
||||||
if (globalRunClock)
|
if (globalRunClock)
|
||||||
for (Clock c : model.getClocks())
|
for (Clock c : model.getClocks())
|
||||||
if (c.getFrequency() > 0) {
|
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;
|
realtimeClockRunning = true;
|
||||||
}
|
}
|
||||||
|
if (modelSync == null)
|
||||||
|
modelSync = NoSync.INST;
|
||||||
|
|
||||||
|
circuitComponent.setModeAndReset(true, modelSync);
|
||||||
|
|
||||||
if (realtimeClockRunning) {
|
if (realtimeClockRunning) {
|
||||||
// if clock is running, enable automatic update of gui
|
// if clock is running, enable automatic update of gui
|
||||||
@ -695,12 +706,12 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
|
|||||||
|
|
||||||
List<String> ordering = circuitComponent.getCircuit().getMeasurementOrdering();
|
List<String> ordering = circuitComponent.getCircuit().getMeasurementOrdering();
|
||||||
if (settings.get(Keys.SHOW_DATA_TABLE))
|
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))
|
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))
|
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;
|
int i = 0;
|
||||||
for (ROM rom : model.findNode(ROM.class))
|
for (ROM rom : model.findNode(ROM.class))
|
||||||
@ -830,6 +841,11 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
|
|||||||
return windowPosManager;
|
return windowPosManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setStatus(String message) {
|
||||||
|
SwingUtilities.invokeLater(() -> statusLabel.setText(message));
|
||||||
|
}
|
||||||
|
|
||||||
private class FullStepObserver implements Observer {
|
private class FullStepObserver implements Observer {
|
||||||
private final Model model;
|
private final Model model;
|
||||||
|
|
||||||
@ -840,8 +856,10 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
|
|||||||
@Override
|
@Override
|
||||||
public void hasChanged() {
|
public void hasChanged() {
|
||||||
try {
|
try {
|
||||||
model.fireManualChangeEvent();
|
modelSync.accessNEx(() -> {
|
||||||
model.doStep();
|
model.fireManualChangeEvent();
|
||||||
|
model.doStep();
|
||||||
|
});
|
||||||
circuitComponent.repaint();
|
circuitComponent.repaint();
|
||||||
} catch (NodeException | RuntimeException e) {
|
} catch (NodeException | RuntimeException e) {
|
||||||
showErrorAndStopModel(Lang.get("msg_errorCalculatingStep"), e);
|
showErrorAndStopModel(Lang.get("msg_errorCalculatingStep"), e);
|
||||||
|
15
src/main/java/de/neemann/digital/gui/StatusInterface.java
Normal file
15
src/main/java/de/neemann/digital/gui/StatusInterface.java
Normal file
@ -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);
|
||||||
|
}
|
@ -13,6 +13,8 @@ import de.neemann.digital.draw.shapes.ShapeFactory;
|
|||||||
import de.neemann.digital.gui.LibrarySelector;
|
import de.neemann.digital.gui.LibrarySelector;
|
||||||
import de.neemann.digital.gui.Main;
|
import de.neemann.digital.gui.Main;
|
||||||
import de.neemann.digital.gui.SavedListener;
|
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.digital.lang.Lang;
|
||||||
import de.neemann.gui.IconCreator;
|
import de.neemann.gui.IconCreator;
|
||||||
import de.neemann.gui.ToolTipAction;
|
import de.neemann.gui.ToolTipAction;
|
||||||
@ -73,6 +75,7 @@ public class CircuitComponent extends JComponent {
|
|||||||
private AffineTransform transform = new AffineTransform();
|
private AffineTransform transform = new AffineTransform();
|
||||||
private Observer manualChangeObserver;
|
private Observer manualChangeObserver;
|
||||||
private Vector lastMousePos;
|
private Vector lastMousePos;
|
||||||
|
private Sync modelSync;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -248,13 +251,15 @@ public class CircuitComponent extends JComponent {
|
|||||||
*
|
*
|
||||||
* @param runMode true if running, false if editing
|
* @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)
|
if (runMode)
|
||||||
mouseRun.activate();
|
mouseRun.activate();
|
||||||
else
|
else {
|
||||||
mouseNormal.activate();
|
mouseNormal.activate();
|
||||||
|
circuit.clearState();
|
||||||
|
}
|
||||||
requestFocusInWindow();
|
requestFocusInWindow();
|
||||||
circuit.clearState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -372,7 +377,7 @@ public class CircuitComponent extends JComponent {
|
|||||||
public void setCircuit(Circuit circuit) {
|
public void setCircuit(Circuit circuit) {
|
||||||
this.circuit = circuit;
|
this.circuit = circuit;
|
||||||
fitCircuit();
|
fitCircuit();
|
||||||
setModeAndReset(false);
|
setModeAndReset(false, NoSync.INST);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -982,7 +987,7 @@ public class CircuitComponent extends JComponent {
|
|||||||
|
|
||||||
|
|
||||||
private interface Actor {
|
private interface Actor {
|
||||||
boolean interact(CircuitComponent cc, Point p);
|
boolean interact(CircuitComponent cc, Point p, Sync modelSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class MouseControllerRun extends MouseController {
|
private final class MouseControllerRun extends MouseController {
|
||||||
@ -1016,7 +1021,7 @@ public class CircuitComponent extends JComponent {
|
|||||||
private void interact(MouseEvent e, Actor actor) {
|
private void interact(MouseEvent e, Actor actor) {
|
||||||
Point p = new Point(e.getX(), e.getY());
|
Point p = new Point(e.getX(), e.getY());
|
||||||
SwingUtilities.convertPointToScreen(p, CircuitComponent.this);
|
SwingUtilities.convertPointToScreen(p, CircuitComponent.this);
|
||||||
boolean modelHasChanged = actor.interact(CircuitComponent.this, p);
|
boolean modelHasChanged = actor.interact(CircuitComponent.this, p, modelSync);
|
||||||
if (modelHasChanged) {
|
if (modelHasChanged) {
|
||||||
if (manualChangeObserver != null)
|
if (manualChangeObserver != null)
|
||||||
manualChangeObserver.hasChanged();
|
manualChangeObserver.hasChanged();
|
||||||
|
@ -3,6 +3,7 @@ package de.neemann.digital.gui.components;
|
|||||||
import de.neemann.digital.core.element.ElementAttributes;
|
import de.neemann.digital.core.element.ElementAttributes;
|
||||||
import de.neemann.digital.core.element.Keys;
|
import de.neemann.digital.core.element.Keys;
|
||||||
import de.neemann.digital.core.memory.DataField;
|
import de.neemann.digital.core.memory.DataField;
|
||||||
|
import de.neemann.digital.gui.sync.Sync;
|
||||||
import de.neemann.digital.lang.Lang;
|
import de.neemann.digital.lang.Lang;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
@ -32,8 +33,8 @@ public class DataEditor extends JDialog {
|
|||||||
* @param parent the parent
|
* @param parent the parent
|
||||||
* @param dataField the data to edit
|
* @param dataField the data to edit
|
||||||
*/
|
*/
|
||||||
public DataEditor(JComponent parent, DataField dataField) {
|
public DataEditor(JComponent parent, DataField dataField, Sync modelSync) {
|
||||||
this(parent, dataField, null);
|
this(parent, dataField, null, modelSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,7 +44,7 @@ public class DataEditor extends JDialog {
|
|||||||
* @param dataField the data to edit
|
* @param dataField the data to edit
|
||||||
* @param attr uset to get bit sizes
|
* @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);
|
super(SwingUtilities.windowForComponent(parent), Lang.get("key_Data"), attr == null ? ModalityType.MODELESS : ModalityType.APPLICATION_MODAL);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
|
|
||||||
@ -70,7 +71,7 @@ public class DataEditor extends JDialog {
|
|||||||
if (size <= 16) cols = 1;
|
if (size <= 16) cols = 1;
|
||||||
else if (size <= 128) cols = 8;
|
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);
|
JTable table = new JTable(dm);
|
||||||
table.setDefaultRenderer(MyLong.class, new MyLongRenderer(bits));
|
table.setDefaultRenderer(MyLong.class, new MyLongRenderer(bits));
|
||||||
getContentPane().add(new JScrollPane(table));
|
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 static class MyTableModel implements TableModel, DataField.DataListener {
|
||||||
private final DataField dataField;
|
private final DataField dataField;
|
||||||
private final int cols;
|
private final int cols;
|
||||||
|
private final Sync modelSync;
|
||||||
private final int rows;
|
private final int rows;
|
||||||
private ArrayList<TableModelListener> listener = new ArrayList<>();
|
private ArrayList<TableModelListener> listener = new ArrayList<>();
|
||||||
|
|
||||||
private MyTableModel(DataField dataField, int cols) {
|
private MyTableModel(DataField dataField, int cols, Sync modelSync) {
|
||||||
this.dataField = dataField;
|
this.dataField = dataField;
|
||||||
this.cols = cols;
|
this.cols = cols;
|
||||||
|
this.modelSync = modelSync;
|
||||||
rows = (dataField.size() - 1) / cols + 1;
|
rows = (dataField.size() - 1) / cols + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,7 +174,9 @@ public class DataEditor extends JDialog {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
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) {
|
private void fireEvent(TableModelEvent e) {
|
||||||
|
@ -8,6 +8,7 @@ import de.neemann.digital.core.memory.DataField;
|
|||||||
import de.neemann.digital.core.memory.ROM;
|
import de.neemann.digital.core.memory.ROM;
|
||||||
import de.neemann.digital.gui.components.test.TestData;
|
import de.neemann.digital.gui.components.test.TestData;
|
||||||
import de.neemann.digital.gui.components.test.TestDataEditor;
|
import de.neemann.digital.gui.components.test.TestDataEditor;
|
||||||
|
import de.neemann.digital.gui.sync.NoSync;
|
||||||
import de.neemann.digital.lang.Lang;
|
import de.neemann.digital.lang.Lang;
|
||||||
import de.neemann.gui.ErrorMessage;
|
import de.neemann.gui.ErrorMessage;
|
||||||
import de.neemann.gui.ToolTipAction;
|
import de.neemann.gui.ToolTipAction;
|
||||||
@ -15,6 +16,7 @@ import de.neemann.gui.language.Bundle;
|
|||||||
import de.neemann.gui.language.Language;
|
import de.neemann.gui.language.Language;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -234,7 +236,7 @@ public final class EditorFactory {
|
|||||||
panel.add(new ToolTipAction(Lang.get("btn_edit")) {
|
panel.add(new ToolTipAction(Lang.get("btn_edit")) {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
DataEditor de = new DataEditor(panel, data, attr);
|
DataEditor de = new DataEditor(panel, data, attr, NoSync.INST);
|
||||||
if (de.showDialog()) {
|
if (de.showDialog()) {
|
||||||
data = de.getDataField();
|
data = de.getDataField();
|
||||||
}
|
}
|
||||||
@ -245,6 +247,7 @@ public final class EditorFactory {
|
|||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
JFileChooser fc = new JFileChooser();
|
JFileChooser fc = new JFileChooser();
|
||||||
fc.setSelectedFile(attr.getFile(ROM.LAST_DATA_FILE_KEY));
|
fc.setSelectedFile(attr.getFile(ROM.LAST_DATA_FILE_KEY));
|
||||||
|
fc.setFileFilter(new FileNameExtensionFilter("hex", "hex"));
|
||||||
if (fc.showOpenDialog(panel) == JFileChooser.APPROVE_OPTION) {
|
if (fc.showOpenDialog(panel) == JFileChooser.APPROVE_OPTION) {
|
||||||
attr.setFile(ROM.LAST_DATA_FILE_KEY, fc.getSelectedFile());
|
attr.setFile(ROM.LAST_DATA_FILE_KEY, fc.getSelectedFile());
|
||||||
try {
|
try {
|
||||||
|
@ -4,6 +4,7 @@ import de.neemann.digital.core.Model;
|
|||||||
import de.neemann.digital.core.ModelEvent;
|
import de.neemann.digital.core.ModelEvent;
|
||||||
import de.neemann.digital.core.ModelStateObserver;
|
import de.neemann.digital.core.ModelStateObserver;
|
||||||
import de.neemann.digital.core.Signal;
|
import de.neemann.digital.core.Signal;
|
||||||
|
import de.neemann.digital.gui.sync.Sync;
|
||||||
import de.neemann.digital.lang.Lang;
|
import de.neemann.digital.lang.Lang;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
@ -27,12 +28,13 @@ public class ProbeDialog extends JDialog implements ModelStateObserver {
|
|||||||
/**
|
/**
|
||||||
* Creates a new instance
|
* Creates a new instance
|
||||||
*
|
*
|
||||||
* @param owner the owner
|
* @param owner the owner
|
||||||
* @param model the model to run
|
* @param model the model to run
|
||||||
* @param type the event type which fires a dialog repaint
|
* @param type the event type which fires a dialog repaint
|
||||||
* @param ordering the names list used to order the measurement values
|
* @param ordering the names list used to order the measurement values
|
||||||
|
* @param modelSync
|
||||||
*/
|
*/
|
||||||
public ProbeDialog(Frame owner, Model model, ModelEvent type, List<String> ordering) {
|
public ProbeDialog(Frame owner, Model model, ModelEvent type, List<String> ordering, Sync modelSync) {
|
||||||
super(owner, Lang.get("win_measures"), false);
|
super(owner, Lang.get("win_measures"), false);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@ -53,12 +55,12 @@ public class ProbeDialog extends JDialog implements ModelStateObserver {
|
|||||||
addWindowListener(new WindowAdapter() {
|
addWindowListener(new WindowAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void windowOpened(WindowEvent e) {
|
public void windowOpened(WindowEvent e) {
|
||||||
model.addObserver(ProbeDialog.this);
|
modelSync.access(() -> model.addObserver(ProbeDialog.this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void windowClosed(WindowEvent e) {
|
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
|
@Override
|
||||||
public void handleEvent(ModelEvent event) {
|
public void handleEvent(ModelEvent event) {
|
||||||
if (event == type || event == ModelEvent.MANUALCHANGE) {
|
if (event == type || event == ModelEvent.MANUALCHANGE) {
|
||||||
tableModel.fireChanged();
|
SwingUtilities.invokeLater(tableModel::fireChanged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package de.neemann.digital.gui.components;
|
package de.neemann.digital.gui.components;
|
||||||
|
|
||||||
import de.neemann.digital.core.ObservableValue;
|
import de.neemann.digital.core.ObservableValue;
|
||||||
|
import de.neemann.digital.gui.sync.Sync;
|
||||||
import de.neemann.digital.lang.Lang;
|
import de.neemann.digital.lang.Lang;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
@ -60,16 +61,20 @@ public final class SingleValueDialog extends JDialog {
|
|||||||
* @param pos the position to pop up the dialog
|
* @param pos the position to pop up the dialog
|
||||||
* @param value the value to edit
|
* @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();
|
String ret = new SingleValueDialog(pos, value.getValueString()).showDialog();
|
||||||
if (ret != null) {
|
if (ret != null) {
|
||||||
ret = ret.trim();
|
ret = ret.trim();
|
||||||
if (ret.equals("?") && value.supportsHighZ()) {
|
if (ret.equals("?") && value.supportsHighZ()) {
|
||||||
value.setHighZ(true);
|
modelSync.access(() -> {
|
||||||
|
value.setHighZ(true);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
long l = Long.decode(ret);
|
long l = Long.decode(ret);
|
||||||
value.set(l, false);
|
modelSync.access(() -> {
|
||||||
|
value.set(l, false);
|
||||||
|
});
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ public class DataSet implements Iterable<DataSample>, Drawable {
|
|||||||
*
|
*
|
||||||
* @param sample the DataSample
|
* @param sample the DataSample
|
||||||
*/
|
*/
|
||||||
public void add(DataSample sample) {
|
synchronized void add(DataSample sample) {
|
||||||
while (samples.size() >= maxSize)
|
while (samples.size() >= maxSize)
|
||||||
samples.remove(0);
|
samples.remove(0);
|
||||||
|
|
||||||
@ -154,7 +154,7 @@ public class DataSet implements Iterable<DataSample>, Drawable {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void drawTo(Graphic g, boolean highLight) {
|
synchronized public void drawTo(Graphic g, boolean highLight) {
|
||||||
int x = getTextBorder();
|
int x = getTextBorder();
|
||||||
|
|
||||||
int yOffs = SIZE / 2;
|
int yOffs = SIZE / 2;
|
||||||
|
@ -5,6 +5,7 @@ import de.neemann.digital.core.ModelEvent;
|
|||||||
import de.neemann.digital.core.ModelStateObserver;
|
import de.neemann.digital.core.ModelStateObserver;
|
||||||
import de.neemann.digital.core.Signal;
|
import de.neemann.digital.core.Signal;
|
||||||
import de.neemann.digital.gui.components.OrderMerger;
|
import de.neemann.digital.gui.components.OrderMerger;
|
||||||
|
import de.neemann.digital.gui.sync.Sync;
|
||||||
import de.neemann.digital.lang.Lang;
|
import de.neemann.digital.lang.Lang;
|
||||||
import de.neemann.gui.ErrorMessage;
|
import de.neemann.gui.ErrorMessage;
|
||||||
import de.neemann.gui.ToolTipAction;
|
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 static final int MAX_SAMPLE_SIZE = 1000;
|
||||||
private final DataSetComponent dsc;
|
private final DataSetComponent dsc;
|
||||||
private final JScrollPane scrollPane;
|
private final JScrollPane scrollPane;
|
||||||
|
private final Sync modelSync;
|
||||||
private DataSet dataSet;
|
private DataSet dataSet;
|
||||||
private DataSetObserver dataSetObserver;
|
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 microStep true the event type which triggers a new DataSample
|
||||||
* @param ordering the ordering of the measurement values
|
* @param ordering the ordering of the measurement values
|
||||||
*/
|
*/
|
||||||
public DataSetDialog(Frame owner, Model model, boolean microStep, List<String> ordering) {
|
public DataSetDialog(Frame owner, Model model, boolean microStep, List<String> ordering, Sync modelSync) {
|
||||||
super(owner, createTitle(microStep), false);
|
super(owner, createTitle(microStep), false);
|
||||||
|
this.modelSync = modelSync;
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
setAlwaysOnTop(true);
|
setAlwaysOnTop(true);
|
||||||
|
|
||||||
@ -64,12 +67,12 @@ public class DataSetDialog extends JDialog implements ModelStateObserver {
|
|||||||
addWindowListener(new WindowAdapter() {
|
addWindowListener(new WindowAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void windowOpened(WindowEvent e) {
|
public void windowOpened(WindowEvent e) {
|
||||||
model.addObserver(DataSetDialog.this);
|
modelSync.access(() -> model.addObserver(DataSetDialog.this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void windowClosed(WindowEvent e) {
|
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
|
@Override
|
||||||
public void handleEvent(ModelEvent event) {
|
public void handleEvent(ModelEvent event) {
|
||||||
dataSetObserver.handleEvent(event);
|
modelSync.access(() -> {
|
||||||
dsc.revalidate();
|
dataSetObserver.handleEvent(event);
|
||||||
dsc.repaint();
|
});
|
||||||
JScrollBar bar = scrollPane.getHorizontalScrollBar();
|
SwingUtilities.invokeLater(() -> {
|
||||||
SwingUtilities.invokeLater(() -> bar.setValue(bar.getMaximum()));
|
dsc.revalidate();
|
||||||
|
dsc.repaint();
|
||||||
|
JScrollBar bar = scrollPane.getHorizontalScrollBar();
|
||||||
|
bar.setValue(bar.getMaximum());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,10 @@ import javax.swing.*;
|
|||||||
import static de.neemann.digital.core.element.PinInfo.input;
|
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
|
* @author hneemann
|
||||||
*/
|
*/
|
||||||
public class GraphicCard extends Node implements Element, RAMInterface {
|
public class GraphicCard extends Node implements Element, RAMInterface {
|
||||||
|
@ -83,7 +83,6 @@ public class ROMListingDialog extends JDialog implements Observer {
|
|||||||
list.setSelectedIndex(line);
|
list.setSelectedIndex(line);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
lastAddr = addr;
|
lastAddr = addr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
41
src/main/java/de/neemann/digital/gui/sync/LockSync.java
Normal file
41
src/main/java/de/neemann/digital/gui/sync/LockSync.java
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
src/main/java/de/neemann/digital/gui/sync/NoSync.java
Normal file
29
src/main/java/de/neemann/digital/gui/sync/NoSync.java
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
34
src/main/java/de/neemann/digital/gui/sync/Sync.java
Normal file
34
src/main/java/de/neemann/digital/gui/sync/Sync.java
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
10
src/main/java/de/neemann/digital/gui/sync/package-info.java
Normal file
10
src/main/java/de/neemann/digital/gui/sync/package-info.java
Normal file
@ -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;
|
Loading…
x
Reference in New Issue
Block a user