diff --git a/src/main/java/de/neemann/digital/core/Model.java b/src/main/java/de/neemann/digital/core/Model.java index 0803d7156..59b032334 100644 --- a/src/main/java/de/neemann/digital/core/Model.java +++ b/src/main/java/de/neemann/digital/core/Model.java @@ -8,11 +8,12 @@ import java.util.Collections; */ public class Model { - private ArrayList nodes; + private final ArrayList nodes; private ArrayList nodesToUpdateAct; private ArrayList nodesToUpdateNext; private int version; private int maxCounter = 1000; + private Listener listener; public Model() { this.nodes = new ArrayList<>(); @@ -66,6 +67,8 @@ public class Model { if (counter++ > maxCounter) { throw new NodeException("seemsToOscillate"); } + if (listener != null) + listener.needsUpdate(); } } @@ -77,4 +80,8 @@ public class Model { nodesToUpdateNext.addAll(nodes); doStep(noise); } + + public void setListener(Listener listener) { + this.listener = listener; + } } diff --git a/src/main/java/de/neemann/digital/core/ObservableValue.java b/src/main/java/de/neemann/digital/core/ObservableValue.java index 73633136f..3030058d4 100644 --- a/src/main/java/de/neemann/digital/core/ObservableValue.java +++ b/src/main/java/de/neemann/digital/core/ObservableValue.java @@ -45,6 +45,7 @@ public class ObservableValue extends Value { } public void setValue(long value) { + value = getValueBits(value); if (this.value != value) { this.value = value; if (!highZ) @@ -52,12 +53,8 @@ public class ObservableValue extends Value { } } - public long getValueBits() { - return getValueBits(getValue()); - } - public long getValueBits(long value) { - return value & (1 << bits) - 1; + return value & ((1 << bits) - 1); } public void checkBits(ObservableValue value) throws BitsException { diff --git a/src/main/java/de/neemann/digital/core/arithmetic/Add.java b/src/main/java/de/neemann/digital/core/arithmetic/Add.java index 2746a4ddd..c539a491c 100644 --- a/src/main/java/de/neemann/digital/core/arithmetic/Add.java +++ b/src/main/java/de/neemann/digital/core/arithmetic/Add.java @@ -36,7 +36,7 @@ public class Add extends Node implements Part { @Override public void readInputs() throws NodeException { - value = a.getValueBits() + b.getValueBits() + c_in.getValueBits(); + value = a.getValue() + b.getValue() + c_in.getValue(); } @Override diff --git a/src/main/java/de/neemann/digital/core/arithmetic/Mul.java b/src/main/java/de/neemann/digital/core/arithmetic/Mul.java index 68999514e..fa519e7bb 100644 --- a/src/main/java/de/neemann/digital/core/arithmetic/Mul.java +++ b/src/main/java/de/neemann/digital/core/arithmetic/Mul.java @@ -28,7 +28,7 @@ public class Mul extends Node implements Part { @Override public void readInputs() throws NodeException { - value = a.getValueBits() * b.getValueBits(); + value = a.getValue() * b.getValue(); } @Override diff --git a/src/main/java/de/neemann/digital/core/arithmetic/Sub.java b/src/main/java/de/neemann/digital/core/arithmetic/Sub.java index 20504309e..4e96a3ac3 100644 --- a/src/main/java/de/neemann/digital/core/arithmetic/Sub.java +++ b/src/main/java/de/neemann/digital/core/arithmetic/Sub.java @@ -26,6 +26,6 @@ public class Sub extends Add { @Override public void readInputs() throws NodeException { - value = a.getValueBits() - b.getValueBits() - c_in.getValueBits(); + value = a.getValue() - b.getValue() - c_in.getValue(); } } diff --git a/src/main/java/de/neemann/digital/core/wiring/Multiplexer.java b/src/main/java/de/neemann/digital/core/wiring/Multiplexer.java index 793517df3..a6ab96881 100644 --- a/src/main/java/de/neemann/digital/core/wiring/Multiplexer.java +++ b/src/main/java/de/neemann/digital/core/wiring/Multiplexer.java @@ -23,7 +23,7 @@ public class Multiplexer extends FanIn { @Override public void readInputs() throws NodeException { - int n = (int) selector.getValueBits(); + int n = (int) selector.getValue(); if (n >= inputs.size()) throw new NodeException("multiplexerSelectsNotPresentInput"); diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java index 33d1597d1..24f39033b 100644 --- a/src/main/java/de/neemann/digital/gui/Main.java +++ b/src/main/java/de/neemann/digital/gui/Main.java @@ -19,16 +19,20 @@ import de.process.utils.gui.ErrorMessage; import de.process.utils.gui.ToolTipAction; import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; import java.awt.*; import java.awt.event.ActionEvent; import java.io.File; +import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.util.prefs.Preferences; /** * @author hneemann */ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave { + private static final Preferences prefs = Preferences.userRoot().node("dig"); private final CircuitComponent circuitComponent; private final InsertHistory insertHistory; private final ToolTipAction save; @@ -40,6 +44,11 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave { Circuit cr = new Circuit(); circuitComponent = new CircuitComponent(cr); + String name = prefs.get("name", null); + if (name != null) { + loadFile(new File(name)); + } + getContentPane().add(circuitComponent); addWindowListener(new ClosingWindowListener(this, this)); @@ -54,14 +63,17 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave { ToolTipAction open = new ToolTipAction("Open") { @Override public void actionPerformed(ActionEvent e) { - + JFileChooser fc = getjFileChooser(); + if (fc.showOpenDialog(Main.this) == JFileChooser.APPROVE_OPTION) { + loadFile(fc.getSelectedFile()); + } } }; ToolTipAction saveas = new ToolTipAction("Save As") { @Override public void actionPerformed(ActionEvent e) { - JFileChooser fc = new JFileChooser(filename == null ? null : filename.getParentFile()); + JFileChooser fc = getjFileChooser(); if (fc.showSaveDialog(Main.this) == JFileChooser.APPROVE_OPTION) { saveFile(fc.getSelectedFile()); } @@ -113,7 +125,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave { @Override public void actionPerformed(ActionEvent e) { try { - ModelDescription m = new ModelDescription(cr); + ModelDescription m = new ModelDescription(circuitComponent.getCircuit()); Model model = m.create(circuitComponent); model.init(true); circuitComponent.setMode(CircuitComponent.Mode.running); @@ -143,6 +155,20 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave { SwingUtilities.invokeLater(() -> new Main().setVisible(true)); } + private static XStream getxStream() { + XStream xStream = new XStream(new StaxDriver()); + xStream.alias("visualPart", VisualPart.class); + xStream.alias("wire", Wire.class); + xStream.alias("circuit", Circuit.class); + return xStream; + } + + private JFileChooser getjFileChooser() { + JFileChooser fileChooser = new JFileChooser(filename == null ? null : filename.getParentFile()); + fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("Circuit", "dig")); + return fileChooser; + } + @Override public boolean isStateChanged() { return false; @@ -153,14 +179,30 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave { save.actionPerformed(null); } - private void saveFile(File filename) { - XStream xStream = new XStream(new StaxDriver()); - xStream.alias("visualPart", VisualPart.class); - xStream.alias("wire", Wire.class); - xStream.alias("circuit", Circuit.class); + private void loadFile(File filename) { + XStream xStream = getxStream(); + try (FileReader in = new FileReader(filename)) { + circuitComponent.setCircuit((Circuit) xStream.fromXML(in)); + setFilename(filename); + } catch (IOException e) { + new ErrorMessage("error writing a file").addCause(e).show(); + } + } + private void setFilename(File filename) { + this.filename = filename; + prefs.put("name", filename.getPath()); + setTitle(filename + " - Digital"); + } + + private void saveFile(File filename) { + if (!filename.getName().endsWith(".dig")) + filename = new File(filename.getPath() + ".dig"); + + XStream xStream = getxStream(); try (FileWriter out = new FileWriter(filename)) { xStream.marshal(circuitComponent.getCircuit(), new PrettyPrintWriter(out)); + setFilename(filename); } catch (IOException e) { new ErrorMessage("error writing a file").addCause(e).show(); } diff --git a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java index 9f13c3dcc..09cb4f0a7 100644 --- a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java +++ b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java @@ -23,11 +23,11 @@ import java.util.ArrayList; public class CircuitComponent extends JComponent implements Listener { private static final String delAction = "myDelAction"; - private final Circuit circuit; + private Circuit circuit; private Mouse listener; - public CircuitComponent(Circuit circuit) { - this.circuit = circuit; + public CircuitComponent(Circuit aCircuit) { + this.circuit = aCircuit; setMode(Mode.part); KeyStroke delKey = KeyStroke.getKeyStroke("DELETE"); @@ -35,7 +35,6 @@ public class CircuitComponent extends JComponent implements Listener { getActionMap().put(delAction, new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { - System.out.println("write"); if (listener instanceof SelectMouseListener) { SelectMouseListener mml = (SelectMouseListener) listener; if (mml.corner1 != null && mml.corner2 != null) { @@ -110,6 +109,11 @@ public class CircuitComponent extends JComponent implements Listener { return circuit; } + public void setCircuit(Circuit circuit) { + this.circuit = circuit; + setMode(Mode.part); + } + public enum Mode {part, wire, running, select} private interface Mouse extends MouseMotionListener, MouseListener { diff --git a/src/main/java/de/neemann/digital/gui/draw/graphics/Style.java b/src/main/java/de/neemann/digital/gui/draw/graphics/Style.java index 786865be7..6534dd3e8 100644 --- a/src/main/java/de/neemann/digital/gui/draw/graphics/Style.java +++ b/src/main/java/de/neemann/digital/gui/draw/graphics/Style.java @@ -7,8 +7,8 @@ import java.awt.*; */ public class Style { public static final Style NORMAL = new Style(2, false, Color.BLACK); - public static final Style WIRE = new Style(2, true, Color.BLUE); - public static final Style WIRE_HIGH = new Style(2, true, new Color(0, 153, 255)); + public static final Style WIRE = new Style(3, true, new Color(0, 112, 0)); + public static final Style WIRE_HIGH = new Style(3, true, new Color(102, 255, 102)); public static final Style FILLED = new Style(2, true, Color.BLACK); public static final Style THIN = new Style(1, false, Color.BLACK); diff --git a/src/main/java/de/neemann/digital/gui/draw/model/Net.java b/src/main/java/de/neemann/digital/gui/draw/model/Net.java index 1f2d9e2c3..47379432d 100644 --- a/src/main/java/de/neemann/digital/gui/draw/model/Net.java +++ b/src/main/java/de/neemann/digital/gui/draw/model/Net.java @@ -16,20 +16,25 @@ public class Net { private final HashSet points; private final ArrayList pins; + private final ArrayList wires; public Net(Wire w) { points = new HashSet<>(); points.add(w.p1); points.add(w.p2); pins = new ArrayList<>(); + wires = new ArrayList<>(); + wires.add(w); } public Vector tryMerge(Wire wire) { if (points.contains(wire.p1)) { + wires.add(wire); points.add(wire.p2); return wire.p2; } if (points.contains(wire.p2)) { + wires.add(wire); points.add(wire.p1); return wire.p1; } @@ -42,6 +47,7 @@ public class Net { public void addAllPointsFrom(Net changedNet) { points.addAll(changedNet.points); + wires.addAll(changedNet.wires); } public void add(Pin pin) { @@ -71,5 +77,9 @@ public class Net { for (Pin i : inputs) { i.setValue(value); } + + for (Wire w : wires) + w.setValue(value); + } } diff --git a/src/main/java/de/neemann/digital/gui/draw/parts/Circuit.java b/src/main/java/de/neemann/digital/gui/draw/parts/Circuit.java index 056733699..cebb05af7 100644 --- a/src/main/java/de/neemann/digital/gui/draw/parts/Circuit.java +++ b/src/main/java/de/neemann/digital/gui/draw/parts/Circuit.java @@ -12,7 +12,7 @@ import java.util.Iterator; * @author hneemann */ public class Circuit implements Drawable { - private static final Vector RAD = new Vector(2, 2); + private static final Vector RAD = new Vector(3, 3); private final ArrayList visualParts; private transient ArrayList dots; private ArrayList wires; @@ -25,10 +25,10 @@ public class Circuit implements Drawable { @Override public void drawTo(Graphic graphic, State state) { + for (Vector d : getDots()) + graphic.drawCircle(d.sub(RAD), d.add(RAD), Style.WIRE); for (Wire w : wires) w.drawTo(graphic, state); - for (Vector d : dots) - graphic.drawCircle(d.sub(RAD), d.add(RAD), Style.WIRE); for (VisualPart p : visualParts) p.drawTo(graphic, state); } @@ -56,7 +56,7 @@ public class Circuit implements Drawable { wires.add(newWire); WireConsistencyChecker checker = new WireConsistencyChecker(wires); wires = checker.check(); - dots = WireConsistencyChecker.createDots(wires); + dots = null; } public ArrayList getParts() { @@ -99,4 +99,10 @@ public class Circuit implements Drawable { public ArrayList getWires() { return wires; } + + public ArrayList getDots() { + if (dots == null) + dots = WireConsistencyChecker.createDots(wires); + return dots; + } } diff --git a/src/main/java/de/neemann/digital/gui/draw/parts/Wire.java b/src/main/java/de/neemann/digital/gui/draw/parts/Wire.java index e814eb758..542803499 100644 --- a/src/main/java/de/neemann/digital/gui/draw/parts/Wire.java +++ b/src/main/java/de/neemann/digital/gui/draw/parts/Wire.java @@ -1,5 +1,6 @@ package de.neemann.digital.gui.draw.parts; +import de.neemann.digital.core.ObservableValue; import de.neemann.digital.gui.draw.graphics.Graphic; import de.neemann.digital.gui.draw.graphics.Style; import de.neemann.digital.gui.draw.graphics.Vector; @@ -12,6 +13,7 @@ public class Wire implements Drawable, Moveable { public Vector p1; public Vector p2; + private ObservableValue value; public Wire(Vector p1, Vector p2) { this.p1 = p1; @@ -20,7 +22,12 @@ public class Wire implements Drawable, Moveable { @Override public void drawTo(Graphic graphic, State state) { - graphic.drawLine(p1, p2, Style.WIRE); + Style style = Style.WIRE; + if (value != null && value.getValue() != 0) { + style = Style.WIRE_HIGH; + } + + graphic.drawLine(p1, p2, style); } @Override @@ -77,5 +84,9 @@ public class Wire implements Drawable, Moveable { '}'; } + public void setValue(ObservableValue value) { + this.value = value; + } + enum Orientation {horzontal, vertical, diagonal} } diff --git a/src/main/java/de/neemann/digital/gui/draw/shapes/GenericShape.java b/src/main/java/de/neemann/digital/gui/draw/shapes/GenericShape.java index 91f2a4e9f..69e398ddd 100644 --- a/src/main/java/de/neemann/digital/gui/draw/shapes/GenericShape.java +++ b/src/main/java/de/neemann/digital/gui/draw/shapes/GenericShape.java @@ -76,6 +76,21 @@ public class GenericShape implements Shape { @Override public Interactor applyStateMonitor(State state, Listener listener, Model model) { + if (symmetric) { + state.getOutput(0).addListener(new Listener() { + public long lastValue = 0; + + @Override + public void needsUpdate() { + long value = state.getInput(0).getValue(); + if (lastValue != value) { + lastValue = value; + listener.needsUpdate(); + } + } + }); + + } return null; } @@ -84,6 +99,11 @@ public class GenericShape implements Shape { int max = Math.max(inputs, outputs); int height = (max - 1) * SIZE + SIZE2; + if (symmetric && state != null) { + graphic.drawText(new Vector(width * SIZE, 0), new Vector((width + 1) * SIZE, 0), Long.toString(state.getOutput(0).getValue())); + } + + if (symmetric && ((inputs & 1) == 0)) height += SIZE; graphic.drawPolygon(new Polygon(true) diff --git a/src/test/java/de/neemann/digital/FlipFlops.java b/src/test/java/de/neemann/digital/FlipFlops.java index 401f20967..914ec23f9 100644 --- a/src/test/java/de/neemann/digital/FlipFlops.java +++ b/src/test/java/de/neemann/digital/FlipFlops.java @@ -32,8 +32,8 @@ public class FlipFlops extends TestCase { s.setValue(0); model.doStep(true); // geht nur mit noise! - assertTrue((a1.getOutput().getValueBits() == 1 && a2.getOutput().getValueBits() == 0) || // endzustand ist undefiniert! - (a1.getOutput().getValueBits() == 0 && a2.getOutput().getValueBits() == 1)); + assertTrue((a1.getOutput().getValue() == 1 && a2.getOutput().getValue() == 0) || // endzustand ist undefiniert! + (a1.getOutput().getValue() == 0 && a2.getOutput().getValue() == 1)); } public void testFlipFlopNAnd() throws Exception { diff --git a/src/test/java/de/neemann/digital/TestExecuter.java b/src/test/java/de/neemann/digital/TestExecuter.java index 64b50d795..743affc59 100644 --- a/src/test/java/de/neemann/digital/TestExecuter.java +++ b/src/test/java/de/neemann/digital/TestExecuter.java @@ -43,7 +43,7 @@ public class TestExecuter { for (int i = 0; i < outputs.length; i++) { int should = val[i + inputs.length]; if (should >= 0) - assertEquals("output " + i, outputs[i].getValueBits(should), outputs[i].getValueBits()); + assertEquals("output " + i, outputs[i].getValueBits(should), outputs[i].getValue()); } }