mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-12 22:36:02 -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.
|
||||
- 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
|
||||
|
@ -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
|
||||
|
@ -14,7 +14,7 @@
|
||||
<int>16</int>
|
||||
</entry>
|
||||
</elementAttributes>
|
||||
<pos x="180" y="220"/>
|
||||
<pos x="200" y="220"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>In</elementName>
|
||||
@ -24,7 +24,7 @@
|
||||
<string>R</string>
|
||||
</entry>
|
||||
</elementAttributes>
|
||||
<pos x="180" y="460"/>
|
||||
<pos x="200" y="460"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>Comparator</elementName>
|
||||
@ -101,7 +101,7 @@
|
||||
<string>C</string>
|
||||
</entry>
|
||||
</elementAttributes>
|
||||
<pos x="180" y="540"/>
|
||||
<pos x="200" y="540"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>And</elementName>
|
||||
@ -160,6 +160,10 @@
|
||||
<visualElement>
|
||||
<elementName>D_FF</elementName>
|
||||
<elementAttributes>
|
||||
<entry>
|
||||
<string>valueIsProbe</string>
|
||||
<boolean>true</boolean>
|
||||
</entry>
|
||||
<entry>
|
||||
<string>Label</string>
|
||||
<string>Bank-FF</string>
|
||||
@ -175,7 +179,7 @@
|
||||
<string>W</string>
|
||||
</entry>
|
||||
</elementAttributes>
|
||||
<pos x="180" y="380"/>
|
||||
<pos x="200" y="380"/>
|
||||
</visualElement>
|
||||
<visualElement>
|
||||
<elementName>Splitter</elementName>
|
||||
@ -256,7 +260,7 @@
|
||||
<p2 x="460" y="520"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="180" y="460"/>
|
||||
<p1 x="200" y="460"/>
|
||||
<p2 x="580" y="460"/>
|
||||
</wire>
|
||||
<wire>
|
||||
@ -264,7 +268,7 @@
|
||||
<p2 x="460" y="300"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="220" y="300"/>
|
||||
<p1 x="240" y="300"/>
|
||||
<p2 x="360" y="300"/>
|
||||
</wire>
|
||||
<wire>
|
||||
@ -292,7 +296,7 @@
|
||||
<p2 x="680" y="180"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="220" y="180"/>
|
||||
<p1 x="240" y="180"/>
|
||||
<p2 x="580" y="180"/>
|
||||
</wire>
|
||||
<wire>
|
||||
@ -324,11 +328,11 @@
|
||||
<p2 x="580" y="220"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="180" y="220"/>
|
||||
<p2 x="220" y="220"/>
|
||||
<p1 x="200" y="220"/>
|
||||
<p2 x="240" y="220"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="220" y="220"/>
|
||||
<p1 x="240" y="220"/>
|
||||
<p2 x="360" y="220"/>
|
||||
</wire>
|
||||
<wire>
|
||||
@ -336,7 +340,7 @@
|
||||
<p2 x="720" y="380"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="180" y="380"/>
|
||||
<p1 x="200" y="380"/>
|
||||
<p2 x="400" y="380"/>
|
||||
</wire>
|
||||
<wire>
|
||||
@ -348,7 +352,7 @@
|
||||
<p2 x="580" y="540"/>
|
||||
</wire>
|
||||
<wire>
|
||||
<p1 x="180" y="540"/>
|
||||
<p1 x="200" y="540"/>
|
||||
<p2 x="340" y="540"/>
|
||||
</wire>
|
||||
<wire>
|
||||
@ -367,6 +371,14 @@
|
||||
<p1 x="400" y="380"/>
|
||||
<p2 x="400" y="560"/>
|
||||
</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>
|
||||
<p1 x="820" y="400"/>
|
||||
<p2 x="820" y="600"/>
|
||||
@ -395,14 +407,6 @@
|
||||
<p1 x="680" y="180"/>
|
||||
<p2 x="680" y="360"/>
|
||||
</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>
|
||||
<p1 x="540" y="520"/>
|
||||
<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"};
|
||||
|
||||
private final TruthTable truthTable;
|
||||
private ArrayList<TableModelListener> listeners = new ArrayList<>();
|
||||
private final ArrayList<TableModelListener> listeners = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
|
@ -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();
|
||||
|
@ -77,7 +77,7 @@ public final class Keys {
|
||||
*/
|
||||
public static final Key<Integer> 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
|
||||
|
@ -20,7 +20,7 @@ public class DataField {
|
||||
private long[] data;
|
||||
private final int bits;
|
||||
|
||||
private transient ArrayList<DataListener> listeners;
|
||||
private final transient ArrayList<DataListener> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -4,24 +4,28 @@ import de.neemann.digital.core.*;
|
||||
import de.neemann.digital.core.wiring.Clock;
|
||||
import de.neemann.digital.gui.ErrorStopper;
|
||||
import de.neemann.digital.gui.GuiModelObserver;
|
||||
import de.neemann.digital.gui.StatusInterface;
|
||||
import de.neemann.digital.gui.sync.Sync;
|
||||
import de.neemann.digital.lang.Lang;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* The real time clock which is used to fire the models clocks with realtime signals
|
||||
*
|
||||
* @author hneemann
|
||||
*/
|
||||
public class RealTimeClock implements ModelStateObserver {
|
||||
private final Model model;
|
||||
private final ScheduledThreadPoolExecutor executor;
|
||||
private final ErrorStopper stopper;
|
||||
private final Sync modelSync;
|
||||
private final StatusInterface status;
|
||||
private final int frequency;
|
||||
private final ObservableValue output;
|
||||
private ScheduledFuture<?> timer;
|
||||
private Runner runner;
|
||||
|
||||
/**
|
||||
* Creates a new real time clock
|
||||
@ -29,11 +33,14 @@ public class RealTimeClock implements ModelStateObserver {
|
||||
* @param model the model
|
||||
* @param clock the clock element which is modify
|
||||
* @param executor the executor used to schedule the update
|
||||
* @param status allows sending messages to the status line
|
||||
*/
|
||||
public RealTimeClock(Model model, Clock clock, ScheduledThreadPoolExecutor executor, ErrorStopper stopper) {
|
||||
public RealTimeClock(Model model, Clock clock, ScheduledThreadPoolExecutor executor, ErrorStopper stopper, Sync modelSync, StatusInterface status) {
|
||||
this.model = model;
|
||||
this.executor = executor;
|
||||
this.stopper = stopper;
|
||||
this.modelSync = modelSync;
|
||||
this.status = status;
|
||||
int f = clock.getFrequency();
|
||||
if (f < 1) f = 1;
|
||||
this.frequency = f;
|
||||
@ -44,27 +51,96 @@ public class RealTimeClock implements ModelStateObserver {
|
||||
public void handleEvent(ModelEvent event) {
|
||||
switch (event) {
|
||||
case STARTED:
|
||||
if (frequency > 50) // if frequency is high it is not necessary to update the GUI at every clock
|
||||
output.removeObserver(GuiModelObserver.class);
|
||||
if (frequency > 50) // if frequency is high it is not necessary to update the GUI at every clock change
|
||||
modelSync.access(() -> output.removeObserver(GuiModelObserver.class));
|
||||
|
||||
int delay = 500 / frequency;
|
||||
if (delay < 1) delay = 1;
|
||||
int delay = 500000 / frequency;
|
||||
if (delay < 10)
|
||||
runner = new ThreadRunner();
|
||||
else
|
||||
runner = new RealTimeRunner(delay);
|
||||
break;
|
||||
case STOPPED:
|
||||
if (runner != null)
|
||||
runner.stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
timer = executor.scheduleAtFixedRate(() -> SwingUtilities.invokeLater(() -> {
|
||||
output.setValue(1 - output.getValue());
|
||||
interface Runner {
|
||||
void stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* runs with defined rate
|
||||
*/
|
||||
private class RealTimeRunner implements Runner {
|
||||
|
||||
private final ScheduledFuture<?> timer;
|
||||
|
||||
RealTimeRunner(int delay) {
|
||||
timer = executor.scheduleAtFixedRate(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
model.doStep();
|
||||
modelSync.accessNEx(() -> {
|
||||
output.setValue(1 - output.getValue());
|
||||
model.doStep();
|
||||
});
|
||||
} catch (NodeException e1) {
|
||||
stopper.showErrorAndStopModel(Lang.get("msg_clockError"), e1);
|
||||
timer.cancel(false);
|
||||
}
|
||||
}), delay, delay, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}, delay, delay, TimeUnit.MICROSECONDS);
|
||||
}
|
||||
|
||||
break;
|
||||
case STOPPED:
|
||||
if (timer != null)
|
||||
timer.cancel(false);
|
||||
break;
|
||||
@Override
|
||||
public void stop() {
|
||||
if (timer != null)
|
||||
timer.cancel(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* runs at fast as possible!
|
||||
*/
|
||||
private class ThreadRunner implements Runner {
|
||||
|
||||
private final Thread thread;
|
||||
|
||||
ThreadRunner() {
|
||||
thread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println("thread start");
|
||||
long time = System.currentTimeMillis();
|
||||
long counter = 0;
|
||||
try {
|
||||
while (!interrupted()) {
|
||||
modelSync.accessNEx(() -> {
|
||||
output.setValue(1 - output.getValue());
|
||||
model.doStep();
|
||||
});
|
||||
counter++;
|
||||
}
|
||||
} catch (NodeException e1) {
|
||||
stopper.showErrorAndStopModel(Lang.get("msg_clockError"), e1);
|
||||
}
|
||||
time = System.currentTimeMillis() - time;
|
||||
|
||||
status.setStatus(counter / time / 2 + "kHz");
|
||||
|
||||
System.out.println("thread end");
|
||||
}
|
||||
};
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
thread.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import de.neemann.digital.core.ModelStateObserver;
|
||||
import de.neemann.digital.core.Observer;
|
||||
import de.neemann.digital.gui.components.CircuitComponent;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* This observer is added to the model if real time timers are started.
|
||||
* This observer paints the CircuitComponent after a step is calculated.
|
||||
@ -18,6 +20,7 @@ public class GuiModelObserver implements Observer, ModelStateObserver {
|
||||
private final CircuitComponent component;
|
||||
private final ModelEvent type;
|
||||
private boolean changed = false;
|
||||
private volatile boolean paintPending;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
@ -38,7 +41,13 @@ public class GuiModelObserver implements Observer, ModelStateObserver {
|
||||
@Override
|
||||
public void handleEvent(ModelEvent event) {
|
||||
if (changed && event == type) {
|
||||
component.paintImmediately(0, 0, component.getWidth(), component.getHeight());
|
||||
if (!paintPending) {
|
||||
paintPending = true;
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
paintPending = false;
|
||||
component.paintImmediately(0, 0, component.getWidth(), component.getHeight());
|
||||
});
|
||||
}
|
||||
changed = false;
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,9 @@ import de.neemann.digital.gui.remote.DigitalHandler;
|
||||
import de.neemann.digital.gui.remote.RemoteSever;
|
||||
import de.neemann.digital.gui.state.State;
|
||||
import de.neemann.digital.gui.state.StateManager;
|
||||
import de.neemann.digital.gui.sync.LockSync;
|
||||
import de.neemann.digital.gui.sync.NoSync;
|
||||
import de.neemann.digital.gui.sync.Sync;
|
||||
import de.neemann.digital.lang.Lang;
|
||||
import de.neemann.gui.*;
|
||||
|
||||
@ -56,7 +59,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
*
|
||||
* @author hneemann
|
||||
*/
|
||||
public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, ErrorStopper, FileHistory.OpenInterface, DigitalRemoteInterface {
|
||||
public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, ErrorStopper, FileHistory.OpenInterface, DigitalRemoteInterface, StatusInterface {
|
||||
private static final ArrayList<Key> ATTR_LIST = new ArrayList<>();
|
||||
private static boolean experimental;
|
||||
|
||||
@ -106,7 +109,9 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
|
||||
private File filename;
|
||||
private FileHistory fileHistory;
|
||||
|
||||
private Sync modelSync;
|
||||
private Model model;
|
||||
|
||||
private ModelCreator modelCreator;
|
||||
private boolean realtimeClockRunning;
|
||||
|
||||
@ -615,7 +620,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
|
||||
public void enter() {
|
||||
super.enter();
|
||||
clearModelDescription();
|
||||
circuitComponent.setModeAndReset(false);
|
||||
circuitComponent.setModeAndReset(false, NoSync.INST);
|
||||
doStep.setEnabled(false);
|
||||
runToBreakAction.setEnabled(false);
|
||||
}
|
||||
@ -662,7 +667,6 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
|
||||
private boolean createAndStartModel(boolean globalRunClock, ModelEvent updateEvent) {
|
||||
try {
|
||||
circuitComponent.removeHighLighted();
|
||||
circuitComponent.setModeAndReset(true);
|
||||
|
||||
modelCreator = new ModelCreator(circuitComponent.getCircuit(), library);
|
||||
|
||||
@ -674,12 +678,19 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
|
||||
statusLabel.setText(Lang.get("msg_N_nodes", model.size()));
|
||||
|
||||
realtimeClockRunning = false;
|
||||
modelSync = null;
|
||||
if (globalRunClock)
|
||||
for (Clock c : model.getClocks())
|
||||
if (c.getFrequency() > 0) {
|
||||
model.addObserver(new RealTimeClock(model, c, timerExecuter, this));
|
||||
if (modelSync == null)
|
||||
modelSync = new LockSync();
|
||||
model.addObserver(new RealTimeClock(model, c, timerExecuter, this, modelSync, this));
|
||||
realtimeClockRunning = true;
|
||||
}
|
||||
if (modelSync == null)
|
||||
modelSync = NoSync.INST;
|
||||
|
||||
circuitComponent.setModeAndReset(true, modelSync);
|
||||
|
||||
if (realtimeClockRunning) {
|
||||
// if clock is running, enable automatic update of gui
|
||||
@ -695,12 +706,12 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
|
||||
|
||||
List<String> ordering = circuitComponent.getCircuit().getMeasurementOrdering();
|
||||
if (settings.get(Keys.SHOW_DATA_TABLE))
|
||||
windowPosManager.register("probe", new ProbeDialog(this, model, updateEvent, ordering)).setVisible(true);
|
||||
windowPosManager.register("probe", new ProbeDialog(this, model, updateEvent, ordering, modelSync)).setVisible(true);
|
||||
|
||||
if (settings.get(Keys.SHOW_DATA_GRAPH))
|
||||
windowPosManager.register("dataset", new DataSetDialog(this, model, updateEvent == ModelEvent.MICROSTEP, ordering)).setVisible(true);
|
||||
windowPosManager.register("dataset", new DataSetDialog(this, model, updateEvent == ModelEvent.MICROSTEP, ordering, modelSync)).setVisible(true);
|
||||
if (settings.get(Keys.SHOW_DATA_GRAPH_MICRO))
|
||||
windowPosManager.register("datasetMicro", new DataSetDialog(this, model, true, ordering)).setVisible(true);
|
||||
windowPosManager.register("datasetMicro", new DataSetDialog(this, model, true, ordering, modelSync)).setVisible(true);
|
||||
|
||||
int i = 0;
|
||||
for (ROM rom : model.findNode(ROM.class))
|
||||
@ -830,6 +841,11 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
|
||||
return windowPosManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(String message) {
|
||||
SwingUtilities.invokeLater(() -> statusLabel.setText(message));
|
||||
}
|
||||
|
||||
private class FullStepObserver implements Observer {
|
||||
private final Model model;
|
||||
|
||||
@ -840,8 +856,10 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
|
||||
@Override
|
||||
public void hasChanged() {
|
||||
try {
|
||||
model.fireManualChangeEvent();
|
||||
model.doStep();
|
||||
modelSync.accessNEx(() -> {
|
||||
model.fireManualChangeEvent();
|
||||
model.doStep();
|
||||
});
|
||||
circuitComponent.repaint();
|
||||
} catch (NodeException | RuntimeException e) {
|
||||
showErrorAndStopModel(Lang.get("msg_errorCalculatingStep"), e);
|
||||
|
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.Main;
|
||||
import de.neemann.digital.gui.SavedListener;
|
||||
import de.neemann.digital.gui.sync.NoSync;
|
||||
import de.neemann.digital.gui.sync.Sync;
|
||||
import de.neemann.digital.lang.Lang;
|
||||
import de.neemann.gui.IconCreator;
|
||||
import de.neemann.gui.ToolTipAction;
|
||||
@ -73,6 +75,7 @@ public class CircuitComponent extends JComponent {
|
||||
private AffineTransform transform = new AffineTransform();
|
||||
private Observer manualChangeObserver;
|
||||
private Vector lastMousePos;
|
||||
private Sync modelSync;
|
||||
|
||||
|
||||
/**
|
||||
@ -248,13 +251,15 @@ public class CircuitComponent extends JComponent {
|
||||
*
|
||||
* @param runMode true if running, false if editing
|
||||
*/
|
||||
public void setModeAndReset(boolean runMode) {
|
||||
public void setModeAndReset(boolean runMode, Sync modelSync) {
|
||||
this.modelSync = modelSync;
|
||||
if (runMode)
|
||||
mouseRun.activate();
|
||||
else
|
||||
else {
|
||||
mouseNormal.activate();
|
||||
circuit.clearState();
|
||||
}
|
||||
requestFocusInWindow();
|
||||
circuit.clearState();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -372,7 +377,7 @@ public class CircuitComponent extends JComponent {
|
||||
public void setCircuit(Circuit circuit) {
|
||||
this.circuit = circuit;
|
||||
fitCircuit();
|
||||
setModeAndReset(false);
|
||||
setModeAndReset(false, NoSync.INST);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -982,7 +987,7 @@ public class CircuitComponent extends JComponent {
|
||||
|
||||
|
||||
private interface Actor {
|
||||
boolean interact(CircuitComponent cc, Point p);
|
||||
boolean interact(CircuitComponent cc, Point p, Sync modelSync);
|
||||
}
|
||||
|
||||
private final class MouseControllerRun extends MouseController {
|
||||
@ -1016,7 +1021,7 @@ public class CircuitComponent extends JComponent {
|
||||
private void interact(MouseEvent e, Actor actor) {
|
||||
Point p = new Point(e.getX(), e.getY());
|
||||
SwingUtilities.convertPointToScreen(p, CircuitComponent.this);
|
||||
boolean modelHasChanged = actor.interact(CircuitComponent.this, p);
|
||||
boolean modelHasChanged = actor.interact(CircuitComponent.this, p, modelSync);
|
||||
if (modelHasChanged) {
|
||||
if (manualChangeObserver != null)
|
||||
manualChangeObserver.hasChanged();
|
||||
|
@ -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<TableModelListener> 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) {
|
||||
|
@ -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 {
|
||||
|
@ -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<String> ordering) {
|
||||
public ProbeDialog(Frame owner, Model model, ModelEvent type, List<String> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ public class DataSet implements Iterable<DataSample>, 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<DataSample>, Drawable {
|
||||
|
||||
|
||||
@Override
|
||||
public void drawTo(Graphic g, boolean highLight) {
|
||||
synchronized public void drawTo(Graphic g, boolean highLight) {
|
||||
int x = getTextBorder();
|
||||
|
||||
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.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<String> ordering) {
|
||||
public DataSetDialog(Frame owner, Model model, boolean microStep, List<String> 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());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -83,7 +83,6 @@ public class ROMListingDialog extends JDialog implements Observer {
|
||||
list.setSelectedIndex(line);
|
||||
});
|
||||
}
|
||||
|
||||
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