diff --git a/src/main/dig/combinatorial/FullAdderRCSig.dig b/src/main/dig/combinatorial/FullAdderRCSig.dig
index bd408a33d..7b4605042 100644
--- a/src/main/dig/combinatorial/FullAdderRCSig.dig
+++ b/src/main/dig/combinatorial/FullAdderRCSig.dig
@@ -8,6 +8,10 @@
{{de 4-Bit Ripple-Carry addierer bestehend aus vier
Volladdierern.}}
+
+ showDataGraphMicro
+ true
+
@@ -692,4 +696,20 @@ end loop
+
+ C_-1
+ A_0
+ A_1
+ A_2
+ A_3
+ B_0
+ B_1
+ B_2
+ B_3
+ S_0
+ S_1
+ S_2
+ S_3
+ C_3
+
\ No newline at end of file
diff --git a/src/main/java/de/neemann/digital/core/Model.java b/src/main/java/de/neemann/digital/core/Model.java
index c953fa3d8..051cdf0e8 100644
--- a/src/main/java/de/neemann/digital/core/Model.java
+++ b/src/main/java/de/neemann/digital/core/Model.java
@@ -209,7 +209,7 @@ public class Model implements Iterable, SyncAccess {
* Closes the model.
* A STOPPED event is fired.
*/
- public void close() {
+ public synchronized void close() {
if (state != State.CLOSED) {
state = State.CLOSED;
int obs = observers.size();
@@ -463,7 +463,7 @@ public class Model implements Iterable, SyncAccess {
* @param event the mandatory event
* @param events more optional events
*/
- public void addObserver(ModelStateObserver observer, ModelEventType event, ModelEventType... events) {
+ public synchronized void addObserver(ModelStateObserver observer, ModelEventType event, ModelEventType... events) {
addObserverForEvent(observer, event);
for (ModelEventType ev : events)
addObserverForEvent(observer, ev);
@@ -474,13 +474,12 @@ public class Model implements Iterable, SyncAccess {
*
* @param observer the observer to add
*/
- public void addObserver(ModelStateObserverTyped observer) {
+ public synchronized void addObserver(ModelStateObserverTyped observer) {
for (ModelEventType ev : observer.getEvents())
addObserverForEvent(observer, ev);
}
-
- private void addObserverForEvent(ModelStateObserver observer, ModelEventType event) {
+ private synchronized void addObserverForEvent(ModelStateObserver observer, ModelEventType event) {
ArrayList obs = observers;
if (event == ModelEventType.STEP || event == ModelEventType.CHECKBURN) {
if (observersStep == null)
@@ -501,7 +500,7 @@ public class Model implements Iterable, SyncAccess {
*
* @param observer the observer to remove
*/
- public void removeObserver(ModelStateObserver observer) {
+ public synchronized void removeObserver(ModelStateObserver observer) {
observers.remove(observer);
if (observersStep != null)
observersStep.remove(observer);
@@ -516,7 +515,7 @@ public class Model implements Iterable, SyncAccess {
* @param the type of the observer
* @return the found observer or null if not present
*/
- public T getObserver(Class observerClass) {
+ public synchronized T getObserver(Class observerClass) {
for (ModelStateObserver mso : observers)
if (mso.getClass() == observerClass)
return (T) mso;
@@ -908,10 +907,40 @@ public class Model implements Iterable, SyncAccess {
synchronized (this) {
run.run();
}
- fireEvent(ModelEvent.EXTERNALCHANGE);
+ fireEvent(ModelEvent.MICROSTEP); // record the external modification as a micro step!
+ doStep();
return run;
}
+ /**
+ * Creates a {@link SyncAccess} instance to access the model.
+ * If microStep is true, there is no foll step performed, in case of a user interaction.
+ *
+ * @param microStep if true no full step is calculated at a user interaction
+ * @return the instance
+ */
+ public SyncAccess createSync(boolean microStep) {
+ if (microStep) {
+ return new SyncAccess() {
+ @Override
+ public A modify(A run) {
+ synchronized (this) {
+ run.run();
+ }
+ fireEvent(ModelEvent.MICROSTEP); // record the external modification as a micro step!
+ return run;
+ }
+
+ @Override
+ public A read(A run) {
+ return Model.this.read(run);
+ }
+ };
+ } else
+ return this;
+ }
+
+
@Override
public A read(A run) {
synchronized (this) {
diff --git a/src/main/java/de/neemann/digital/core/ModelEvent.java b/src/main/java/de/neemann/digital/core/ModelEvent.java
index 5d038b848..52026242d 100644
--- a/src/main/java/de/neemann/digital/core/ModelEvent.java
+++ b/src/main/java/de/neemann/digital/core/ModelEvent.java
@@ -43,10 +43,6 @@ public class ModelEvent {
* Shorthand for a ModelEventType.BREAK event
*/
public static final ModelEvent BREAK = new ModelEvent(ModelEventType.BREAK);
- /**
- * Shorthand for a ModelEventType.EXTERNALCHANGE event
- */
- public static final ModelEvent EXTERNALCHANGE = new ModelEvent(ModelEventType.EXTERNALCHANGE);
private final ModelEventType type;
private Exception cause;
diff --git a/src/main/java/de/neemann/digital/core/ModelEventType.java b/src/main/java/de/neemann/digital/core/ModelEventType.java
index ccdaa9989..6f26a4649 100644
--- a/src/main/java/de/neemann/digital/core/ModelEventType.java
+++ b/src/main/java/de/neemann/digital/core/ModelEventType.java
@@ -42,11 +42,6 @@ public enum ModelEventType {
*/
BREAK,
- /**
- * Here was a manual change to the model by the user.
- */
- EXTERNALCHANGE,
-
/**
* Is fired if a micro step is calculated.
* This means the aktual nodes are calculated, but not the effected nodes.
diff --git a/src/main/java/de/neemann/digital/data/DataPlotter.java b/src/main/java/de/neemann/digital/data/DataPlotter.java
index 5cb0d98ff..8ed18f5fc 100644
--- a/src/main/java/de/neemann/digital/data/DataPlotter.java
+++ b/src/main/java/de/neemann/digital/data/DataPlotter.java
@@ -57,7 +57,7 @@ public class DataPlotter implements Drawable {
* Fits the data in the visible area
*/
public void fitInside() {
- modelSync.modify(() -> size = ((double) (width - textWidth)) / dataOriginal.getRows());
+ modelSync.read(() -> size = ((double) (width - textWidth)) / dataOriginal.getRows());
offset = 0;
manualScaling = false;
}
diff --git a/src/main/java/de/neemann/digital/draw/model/RealTimeClock.java b/src/main/java/de/neemann/digital/draw/model/RealTimeClock.java
index 53e840ea1..eb71701ff 100644
--- a/src/main/java/de/neemann/digital/draw/model/RealTimeClock.java
+++ b/src/main/java/de/neemann/digital/draw/model/RealTimeClock.java
@@ -97,7 +97,6 @@ public class RealTimeClock implements ModelStateObserverTyped {
frequencyCalculator = null;
timer = executor.scheduleAtFixedRate(() -> {
model.modify(() -> output.setValue(1 - output.getValue()));
- model.doStep();
if (frequencyCalculator != null)
frequencyCalculator.calc();
}, delay, delay, TimeUnit.MICROSECONDS);
@@ -123,7 +122,6 @@ public class RealTimeClock implements ModelStateObserverTyped {
FrequencyCalculator frequencyCalculator = new FrequencyCalculator(status, frequency);
while (!Thread.interrupted()) {
model.modify(() -> output.setValue(1 - output.getValue()));
- model.doStep();
frequencyCalculator.calc();
}
});
diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java
index 41eccc523..2dfa13533 100644
--- a/src/main/java/de/neemann/digital/gui/Main.java
+++ b/src/main/java/de/neemann/digital/gui/Main.java
@@ -1382,7 +1382,7 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
private void clearModelDescription() {
if (model != null)
- model.modify(() -> model.close());
+ model.close();
modelCreator = null;
model = null;
@@ -1397,11 +1397,9 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
modelCreator = new ModelCreator(circuitComponent.getCircuit(), library);
if (model != null) {
- model.modify(() -> {
- ModelClosedObserver mco = model.getObserver(ModelClosedObserver.class);
- if (mco != null) mco.setClosedByRestart(true);
- model.close();
- });
+ ModelClosedObserver mco = model.getObserver(ModelClosedObserver.class);
+ if (mco != null) mco.setClosedByRestart(true);
+ model.close();
circuitComponent.getCircuit().clearState();
model = null;
}
@@ -1448,7 +1446,8 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
}
}
- circuitComponent.setModeAndReset(true, model);
+ circuitComponent.setModeAndReset(true,
+ model.createSync(updateEvent == ModelEventType.MICROSTEP));
if (circuitComponent.getCircuit().getAttributes().get(Keys.IS_GENERIC))
circuitComponent.setCopy(modelCreator.getCircuit());
@@ -1456,12 +1455,12 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
CircuitModifierPostClosed cmpc = new CircuitModifierPostClosed(
modification -> SwingUtilities.invokeLater(() -> circuitComponent.modify(modification)));
modelCreator.connectToGui(cmpc);
- model.addObserver(cmpc);
+ this.model.addObserver(cmpc);
handleKeyboardComponents();
doMicroStep.setEnabled(false);
- if (!realTimeClockRunning && model.isRunToBreakAllowed()) {
+ if (!realTimeClockRunning && this.model.isRunToBreakAllowed()) {
if (updateEvent == ModelEventType.MICROSTEP)
runToBreakMicroAction.setEnabled(true);
else
@@ -1478,30 +1477,30 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
showMeasurementGraph(ModelEventType.MICROSTEP);
if (modelModifier != null)
- modelModifier.preInit(model);
+ modelModifier.preInit(this.model);
else {
if (settings.get(Keys.PRELOAD_PROGRAM)) {
File romHex = new FileLocator(settings.get(Keys.PROGRAM_TO_PRELOAD))
.setupWithMain(this)
.locate();
new ProgramMemoryLoader(romHex)
- .preInit(model);
+ .preInit(this.model);
}
}
if (updateEvent == ModelEventType.MICROSTEP) {
- checkMicroStepActions(model);
- model.addObserver(new MicroStepObserver(model, modelCreator));
+ checkMicroStepActions(this.model);
+ this.model.addObserver(new UpdateViewMicroStepObserver(this.model, modelCreator));
} else if (updateEvent == ModelEventType.STEP) {
if (maxFrequency <= 50)
- model.addObserver(new FullStepObserver(model));
+ this.model.addObserver(new UpdateViewAtEventObserver());
else
- model.addObserver(new FastObserver());
+ this.model.addObserver(new UpdateViewPeriodicObserver());
}
- model.addObserver(new ModelClosedObserver());
+ this.model.addObserver(new ModelClosedObserver());
- model.init();
+ this.model.init();
} catch (NodeException | PinException | RuntimeException | ElementNotFoundException e) {
if (model != null)
@@ -1736,20 +1735,15 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
/**
* Updates the graphic at every modification.
*/
- private class FullStepObserver implements ModelStateObserverTyped {
- private final Model model;
-
- FullStepObserver(Model model) {
- this.model = model;
+ private class UpdateViewAtEventObserver implements ModelStateObserverTyped {
+ UpdateViewAtEventObserver() {
}
@Override
public void handleEvent(ModelEvent event) {
switch (event.getType()) {
- case EXTERNALCHANGE:
- model.doStep();
- circuitComponent.graphicHasChanged();
- break;
+ case CHECKBURN:
+ case STEP:
case BREAK:
circuitComponent.graphicHasChanged();
break;
@@ -1758,17 +1752,17 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
@Override
public ModelEventType[] getEvents() {
- return new ModelEventType[]{ModelEventType.EXTERNALCHANGE, ModelEventType.BREAK};
+ return new ModelEventType[]{ModelEventType.CHECKBURN, ModelEventType.STEP, ModelEventType.BREAK};
}
}
/**
* Updates the graphic at every 100ms
*/
- private final class FastObserver implements ModelStateObserverTyped {
+ private final class UpdateViewPeriodicObserver implements ModelStateObserverTyped {
private final Timer timer;
- private FastObserver() {
+ private UpdateViewPeriodicObserver() {
timer = new Timer(100, actionEvent -> circuitComponent.graphicHasChanged());
}
@@ -1795,11 +1789,11 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
/**
* Updates the graphic at every micro step
*/
- private final class MicroStepObserver implements ModelStateObserverTyped {
+ private final class UpdateViewMicroStepObserver implements ModelStateObserverTyped {
private final Model model;
private final ModelCreator modelCreator;
- private MicroStepObserver(Model model, ModelCreator modelCreator) {
+ private UpdateViewMicroStepObserver(Model model, ModelCreator modelCreator) {
this.model = model;
this.modelCreator = modelCreator;
}
@@ -1807,7 +1801,7 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
@Override
public void handleEvent(ModelEvent event) {
switch (event.getType()) {
- case EXTERNALCHANGE:
+ case CHECKBURN:
case MICROSTEP:
case BREAK:
if (!realTimeClockRunning) {
@@ -1823,7 +1817,7 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
@Override
public ModelEventType[] getEvents() {
- return new ModelEventType[]{ModelEventType.EXTERNALCHANGE, ModelEventType.MICROSTEP, ModelEventType.BREAK};
+ return new ModelEventType[]{ModelEventType.CHECKBURN, ModelEventType.MICROSTEP, ModelEventType.BREAK};
}
}
@@ -1961,12 +1955,9 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
if (cl.size() == 1) {
ObservableValue clkVal = cl.get(0).getClockOutput();
model.modify(() -> clkVal.setBool(!clkVal.getBool()));
- model.doStep();
if (model != null) {
- if (clkVal.getBool() && model.isRunning()) {
+ if (clkVal.getBool() && model.isRunning())
model.modify(() -> clkVal.setBool(!clkVal.getBool()));
- model.doStep();
- }
addressPicker.getProgramROMAddress(model);
}
}
diff --git a/src/main/java/de/neemann/digital/gui/components/ProbeDialog.java b/src/main/java/de/neemann/digital/gui/components/ProbeDialog.java
index 7f2349f8a..30575d26b 100644
--- a/src/main/java/de/neemann/digital/gui/components/ProbeDialog.java
+++ b/src/main/java/de/neemann/digital/gui/components/ProbeDialog.java
@@ -60,12 +60,12 @@ public class ProbeDialog extends JDialog implements ModelStateObserverTyped {
addWindowListener(new WindowAdapter() {
@Override
public void windowOpened(WindowEvent e) {
- model.modify(() -> model.addObserver(ProbeDialog.this));
+ model.addObserver(ProbeDialog.this);
}
@Override
public void windowClosed(WindowEvent e) {
- model.modify(() -> model.removeObserver(ProbeDialog.this));
+ model.removeObserver(ProbeDialog.this);
}
});
@@ -109,7 +109,7 @@ public class ProbeDialog extends JDialog implements ModelStateObserverTyped {
@Override
public void handleEvent(ModelEvent event) {
- if (event.getType() == type || event == ModelEvent.EXTERNALCHANGE) {
+ if (event.getType() == type || event == ModelEvent.CHECKBURN) {
if (tableUpdateEnable) {
if (paintPending.compareAndSet(false, true)) {
SwingUtilities.invokeLater(() -> {
@@ -133,7 +133,7 @@ public class ProbeDialog extends JDialog implements ModelStateObserverTyped {
@Override
public ModelEventType[] getEvents() {
- return new ModelEventType[]{type, ModelEventType.EXTERNALCHANGE, ModelEventType.FASTRUN, ModelEventType.BREAK, ModelEventType.CLOSED};
+ return new ModelEventType[]{type, ModelEventType.CHECKBURN, ModelEventType.FASTRUN, ModelEventType.BREAK, ModelEventType.CLOSED};
}
private static class SignalTableModel implements TableModel {
diff --git a/src/main/java/de/neemann/digital/gui/components/SingleValueDialog.java b/src/main/java/de/neemann/digital/gui/components/SingleValueDialog.java
index 33d86c21b..88f69a4c0 100644
--- a/src/main/java/de/neemann/digital/gui/components/SingleValueDialog.java
+++ b/src/main/java/de/neemann/digital/gui/components/SingleValueDialog.java
@@ -109,11 +109,11 @@ public final class SingleValueDialog extends JDialog implements ModelStateObserv
JPanel checkBoxPanel = createCheckBoxPanel(editValue);
- model.modify(() -> model.addObserver(this));
+ model.addObserver(this);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent windowEvent) {
- model.modify(() -> model.removeObserver(SingleValueDialog.this));
+ model.removeObserver(SingleValueDialog.this);
}
});
diff --git a/src/main/java/de/neemann/digital/gui/components/data/GraphDialog.java b/src/main/java/de/neemann/digital/gui/components/data/GraphDialog.java
index 580603b60..6a4baef00 100644
--- a/src/main/java/de/neemann/digital/gui/components/data/GraphDialog.java
+++ b/src/main/java/de/neemann/digital/gui/components/data/GraphDialog.java
@@ -78,12 +78,12 @@ public class GraphDialog extends JDialog implements Observer {
graphDialog.addWindowListener(new WindowAdapter() {
@Override
public void windowOpened(WindowEvent e) {
- model.modify(() -> model.addObserver(valueTableObserver));
+ model.addObserver(valueTableObserver);
}
@Override
public void windowClosed(WindowEvent e) {
- model.modify(() -> model.removeObserver(valueTableObserver));
+ model.removeObserver(valueTableObserver);
}
});
diff --git a/src/main/java/de/neemann/digital/gui/components/data/ScopeTrigger.java b/src/main/java/de/neemann/digital/gui/components/data/ScopeTrigger.java
index 0b1b6e882..11747f237 100644
--- a/src/main/java/de/neemann/digital/gui/components/data/ScopeTrigger.java
+++ b/src/main/java/de/neemann/digital/gui/components/data/ScopeTrigger.java
@@ -167,7 +167,7 @@ public class ScopeTrigger extends Node implements Element {
graphDialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
- model.modify(() -> model.removeObserver(scopeModelStateObserver));
+ model.removeObserver(scopeModelStateObserver);
}
});
graphDialog.setVisible(true);
diff --git a/src/main/java/de/neemann/digital/gui/components/data/ValueTableObserver.java b/src/main/java/de/neemann/digital/gui/components/data/ValueTableObserver.java
index 8cdc556f8..e290f549c 100644
--- a/src/main/java/de/neemann/digital/gui/components/data/ValueTableObserver.java
+++ b/src/main/java/de/neemann/digital/gui/components/data/ValueTableObserver.java
@@ -24,8 +24,6 @@ public class ValueTableObserver implements ModelStateObserverTyped {
private final ModelEventType type;
private final ArrayList signals;
- private Value[] manualSample;
-
/**
* Creates a new instance
*
@@ -51,18 +49,7 @@ public class ValueTableObserver implements ModelStateObserverTyped {
if (event == ModelEvent.STARTED)
logData.clear();
- if (event == ModelEvent.EXTERNALCHANGE && type == ModelEventType.MICROSTEP) {
- if (manualSample == null)
- manualSample = new Value[logData.getColumns()];
- for (int i = 0; i < logData.getColumns(); i++)
- manualSample[i] = new Value(signals.get(i).getValue());
- }
-
if (event.getType() == type) {
- if (manualSample != null) {
- logData.add(new TestRow(manualSample));
- manualSample = null;
- }
Value[] row = new Value[logData.getColumns()];
for (int i = 0; i < logData.getColumns(); i++)
row[i] = new Value(signals.get(i).getValue());
@@ -72,7 +59,7 @@ public class ValueTableObserver implements ModelStateObserverTyped {
@Override
public ModelEventType[] getEvents() {
- return new ModelEventType[]{type, ModelEventType.STARTED, ModelEventType.EXTERNALCHANGE};
+ return new ModelEventType[]{type, ModelEventType.STARTED};
}
/**
diff --git a/src/main/java/de/neemann/digital/gui/tutorial/InitialTutorial.java b/src/main/java/de/neemann/digital/gui/tutorial/InitialTutorial.java
index 56b736da7..d1daa667d 100644
--- a/src/main/java/de/neemann/digital/gui/tutorial/InitialTutorial.java
+++ b/src/main/java/de/neemann/digital/gui/tutorial/InitialTutorial.java
@@ -228,12 +228,12 @@ public class InitialTutorial extends JDialog implements CircuitComponent.Tutoria
@Override
public ModelEventType[] getEvents() {
- return new ModelEventType[]{ModelEventType.EXTERNALCHANGE};
+ return new ModelEventType[]{ModelEventType.STEP};
}
@Override
public void handleEvent(ModelEvent event) {
- if (event == ModelEvent.EXTERNALCHANGE)
+ if (event == ModelEvent.STEP)
modified(null);
}