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); }