mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-18 09:24:42 -04:00
first version of a splitter
This commit is contained in:
parent
65a6d42014
commit
ed6a2f6a16
@ -11,6 +11,8 @@ public class AttributeKey<VALUE> {
|
||||
public static final AttributeKey<Integer> Value = new AttributeKey<>("Value", 1);
|
||||
public static final AttributeKey<Integer> Default = new AttributeKey<>("Default", 0);
|
||||
public static final AttributeKey<java.awt.Color> Color = new AttributeKey<>("Color", java.awt.Color.RED);
|
||||
public static final AttributeKey<String> InputSplit = new AttributeKey<>("Input Splitting", "");
|
||||
public static final AttributeKey<String> OutputSplit = new AttributeKey<>("Output Splitting", "");
|
||||
|
||||
private final String name;
|
||||
private final VALUE def;
|
||||
|
@ -36,7 +36,7 @@ public class ElementAttributes {
|
||||
}
|
||||
}
|
||||
|
||||
public <VALUE> void set(AttributeKey<VALUE> key, VALUE value) {
|
||||
public <VALUE> ElementAttributes set(AttributeKey<VALUE> key, VALUE value) {
|
||||
if (value != get(key)) {
|
||||
if (value.equals(key.getDefault())) {
|
||||
if (attributes != null)
|
||||
@ -48,6 +48,7 @@ public class ElementAttributes {
|
||||
}
|
||||
fireValueChanged(key);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void fireValueChanged(AttributeKey key) {
|
||||
|
183
src/main/java/de/neemann/digital/core/wiring/Splitter.java
Normal file
183
src/main/java/de/neemann/digital/core/wiring/Splitter.java
Normal file
@ -0,0 +1,183 @@
|
||||
package de.neemann.digital.core.wiring;
|
||||
|
||||
import de.neemann.digital.core.*;
|
||||
import de.neemann.digital.core.element.AttributeKey;
|
||||
import de.neemann.digital.core.element.Element;
|
||||
import de.neemann.digital.core.element.ElementAttributes;
|
||||
import de.neemann.digital.core.element.ElementTypeDescription;
|
||||
import de.neemann.digital.gui.draw.elements.PinException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* @author hneemann
|
||||
*/
|
||||
public class Splitter implements Element {
|
||||
|
||||
public static final ElementTypeDescription DESCRIPTION
|
||||
= new SplitterTypeDescription()
|
||||
.addAttribute(AttributeKey.InputSplit)
|
||||
.addAttribute(AttributeKey.OutputSplit)
|
||||
.setShortName("");
|
||||
|
||||
private final ObservableValue[] outputs;
|
||||
private final Ports inPorts;
|
||||
private final Ports outPorts;
|
||||
private ObservableValue[] inputs;
|
||||
|
||||
|
||||
private static class SplitterTypeDescription extends ElementTypeDescription {
|
||||
public SplitterTypeDescription() {
|
||||
super(Splitter.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getInputNames(ElementAttributes elementAttributes) {
|
||||
Ports p = new Ports(elementAttributes.get(AttributeKey.InputSplit));
|
||||
return p.getNames();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Splitter(ElementAttributes attributes) throws PinException {
|
||||
outPorts = new Ports(attributes.get(AttributeKey.OutputSplit));
|
||||
outputs = outPorts.getOutputs();
|
||||
inPorts = new Ports(attributes.get(AttributeKey.InputSplit));
|
||||
if (inPorts.getBits() != outPorts.getBits())
|
||||
throw new PinException("splitterPortMismatch");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputs(ObservableValue... inputs) throws NodeException {
|
||||
this.inputs = inputs;
|
||||
for (int i = 0; i < inputs.length; i++) {
|
||||
if (inPorts.getPort(i).getBits() != inputs[i].getBits())
|
||||
throw new BitsException("splitterBitsMismatch", inputs[i]);
|
||||
inputs[i].addObserver(createObserverForInput(i));
|
||||
}
|
||||
}
|
||||
|
||||
private Observer createObserverForInput(int i) throws NodeException {
|
||||
Observer observer = outPorts.getSimpleTargetObserver(inPorts.getPort(i), inputs, outputs);
|
||||
if (observer == null)
|
||||
throw new NodeException("splitterMismatchError");
|
||||
return observer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableValue[] getOutputs() {
|
||||
return outputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerNodes(Model model) {
|
||||
}
|
||||
|
||||
public static final class Ports {
|
||||
private final ArrayList<Port> ports;
|
||||
private int bits;
|
||||
|
||||
public Ports(String definition) {
|
||||
StringTokenizer st = new StringTokenizer(definition, ",", false);
|
||||
ports = new ArrayList<>();
|
||||
bits = 0;
|
||||
while (st.hasMoreTokens()) {
|
||||
Port p = new Port(Integer.decode(st.nextToken().trim()), bits, ports.size());
|
||||
ports.add(p);
|
||||
bits += p.getBits();
|
||||
}
|
||||
if (ports.isEmpty()) {
|
||||
ports.add(new Port(1, 0, 0));
|
||||
bits = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int getBits() {
|
||||
return bits;
|
||||
}
|
||||
|
||||
public String[] getNames() {
|
||||
String[] name = new String[ports.size()];
|
||||
for (int i = 0; i < name.length; i++)
|
||||
name[i] = ports.get(i).getName();
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
public ObservableValue[] getOutputs() {
|
||||
ObservableValue[] outputs = new ObservableValue[ports.size()];
|
||||
for (int i = 0; i < ports.size(); i++) {
|
||||
Port p = ports.get(i);
|
||||
outputs[i] = new ObservableValue(p.getName(), p.getBits());
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
|
||||
public Port getPort(int i) {
|
||||
return ports.get(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is a single out target port for the input port
|
||||
*
|
||||
* @param inPort
|
||||
* @param inputs
|
||||
* @param outputs
|
||||
*/
|
||||
public Observer getSimpleTargetObserver(Port inPort, ObservableValue[] inputs, ObservableValue[] outputs) {
|
||||
int pos = inPort.getPos();
|
||||
int bits = inPort.getBits();
|
||||
|
||||
for (Port outPort : ports) {
|
||||
if (outPort.getPos() <= pos && pos + bits <= outPort.getPos() + outPort.getBits()) {
|
||||
final int bitPos = pos - outPort.getPos();
|
||||
final int mask = ~(((1 << inPort.bits) - 1) << bitPos);
|
||||
final ObservableValue inValue = inputs[inPort.number];
|
||||
final ObservableValue outValue = outputs[outPort.number];
|
||||
return new Observer() {
|
||||
@Override
|
||||
public void hasChanged() {
|
||||
long in = inValue.getValue();
|
||||
long out = outValue.getValue();
|
||||
outValue.setValue((out & mask) | (in << bitPos));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class Port {
|
||||
|
||||
private final String name;
|
||||
private final int bits;
|
||||
private final int pos;
|
||||
private final int number;
|
||||
|
||||
public Port(int bits, int pos, int number) {
|
||||
this.bits = bits;
|
||||
this.pos = pos;
|
||||
this.number = number;
|
||||
if (bits == 1)
|
||||
name = "" + pos;
|
||||
else
|
||||
name = "" + pos + "-" + (pos + bits - 1);
|
||||
}
|
||||
|
||||
public int getBits() {
|
||||
return bits;
|
||||
}
|
||||
|
||||
public int getPos() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
@ -101,7 +101,8 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
|
||||
|
||||
shape.drawTo(gr);
|
||||
for (Pin p : shape.getPins())
|
||||
gr.drawCircle(p.getPos().add(-PIN, -PIN), p.getPos().add(PIN, PIN), p.getDirection() == Pin.Direction.input ? Style.NORMAL : Style.WIRE);
|
||||
gr.drawCircle(p.getPos().add(-PIN, -PIN), p.getPos().add(PIN, PIN)
|
||||
, p.getDirection() == Pin.Direction.input ? Style.NORMAL : Style.WIRE_OUT);
|
||||
|
||||
if (highLight && minMax != null) {
|
||||
Vector delta = minMax.getMax().sub(minMax.getMin());
|
||||
|
@ -10,6 +10,7 @@ public class Style {
|
||||
public static final Style WIRE = new Style(2, true, Color.BLUE.darker());
|
||||
public static final Style WIRE_LOW = 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 WIRE_OUT = new Style(2, true, Color.RED.darker());
|
||||
public static final Style FILLED = new Style(2, true, Color.BLACK);
|
||||
public static final Style THIN = new Style(1, false, Color.BLACK);
|
||||
public static final Style DASH = new Style(1, false, Color.BLACK);
|
||||
|
@ -13,6 +13,7 @@ import de.neemann.digital.core.io.Const;
|
||||
import de.neemann.digital.core.io.In;
|
||||
import de.neemann.digital.core.io.Out;
|
||||
import de.neemann.digital.core.wiring.Delay;
|
||||
import de.neemann.digital.core.wiring.Splitter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@ -42,6 +43,8 @@ public class PartLibrary implements Iterable<PartLibrary.PartContainer> {
|
||||
add(Out.LEDDESCRIPTION, "IO");
|
||||
add(Out.PROBEDESCRIPTION, "IO");
|
||||
|
||||
add(Splitter.DESCRIPTION, "Wires");
|
||||
|
||||
add(RS_FF.DESCRIPTION, "FlipFlops");
|
||||
add(JK_FF.DESCRIPTION, "FlipFlops");
|
||||
add(D_FF.DESCRIPTION, "FlipFlops");
|
||||
|
@ -26,7 +26,7 @@ public class ModelEntry {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Inputs of the element contained in thes entry
|
||||
* Sets the Inputs of the element contained in this entry
|
||||
*
|
||||
* @throws PinException
|
||||
* @throws NodeException
|
||||
|
@ -12,6 +12,7 @@ import de.neemann.digital.core.io.Const;
|
||||
import de.neemann.digital.core.io.In;
|
||||
import de.neemann.digital.core.io.Out;
|
||||
import de.neemann.digital.core.wiring.Delay;
|
||||
import de.neemann.digital.core.wiring.Splitter;
|
||||
import de.neemann.digital.gui.draw.library.PartLibrary;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -53,6 +54,9 @@ public final class ShapeFactory {
|
||||
map.put(Out.DESCRIPTION.getName(), attr -> new OutputShape(attr.get(AttributeKey.Label)));
|
||||
map.put(Out.LEDDESCRIPTION.getName(), attr -> new LEDShape(attr.get(AttributeKey.Label), attr.get(AttributeKey.Color)));
|
||||
map.put(Out.PROBEDESCRIPTION.getName(), attr -> new ProbeShape(attr.get(AttributeKey.Label)));
|
||||
|
||||
map.put(Splitter.DESCRIPTION.getName(), attr -> new SplitterShape(attr.get(AttributeKey.InputSplit), attr.get(AttributeKey.OutputSplit)));
|
||||
|
||||
}
|
||||
|
||||
public PartLibrary setLibrary(PartLibrary library) {
|
||||
|
@ -0,0 +1,62 @@
|
||||
package de.neemann.digital.gui.draw.shapes;
|
||||
|
||||
import de.neemann.digital.core.Observer;
|
||||
import de.neemann.digital.core.wiring.Splitter;
|
||||
import de.neemann.digital.gui.draw.elements.IOState;
|
||||
import de.neemann.digital.gui.draw.elements.Pin;
|
||||
import de.neemann.digital.gui.draw.elements.Pins;
|
||||
import de.neemann.digital.gui.draw.graphics.*;
|
||||
|
||||
import static de.neemann.digital.gui.draw.shapes.GenericShape.SIZE;
|
||||
|
||||
/**
|
||||
* @author hneemann
|
||||
*/
|
||||
public class SplitterShape implements Shape {
|
||||
|
||||
private final String[] inputs;
|
||||
private final String[] outputs;
|
||||
private final int length;
|
||||
private Pins pins;
|
||||
|
||||
public SplitterShape(String inputDef, String outputDef) {
|
||||
inputs = new Splitter.Ports(inputDef).getNames();
|
||||
outputs = new Splitter.Ports(outputDef).getNames();
|
||||
length = (Math.max(inputs.length, outputs.length) - 1) * SIZE + 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pins getPins() {
|
||||
if (pins == null) {
|
||||
pins = new Pins();
|
||||
for (int i = 0; i < inputs.length; i++)
|
||||
pins.add(new Pin(new Vector(0, i * SIZE), inputs[i], Pin.Direction.input));
|
||||
for (int i = 0; i < outputs.length; i++)
|
||||
pins.add(new Pin(new Vector(SIZE, i * SIZE), outputs[i], Pin.Direction.output));
|
||||
}
|
||||
return pins;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Interactor applyStateMonitor(IOState ioState, Observer guiObserver) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTo(Graphic graphic) {
|
||||
for (int i = 0; i < inputs.length; i++) {
|
||||
Vector pos = new Vector(-2, i * SIZE - 2);
|
||||
graphic.drawText(pos, pos.add(2, 0), inputs[i], Orientation.RIGHTBOTTOM, Style.SHAPE_PIN);
|
||||
}
|
||||
for (int i = 0; i < outputs.length; i++) {
|
||||
Vector pos = new Vector(SIZE + 2, i * SIZE - 2);
|
||||
graphic.drawText(pos, pos.add(2, 0), outputs[i], Orientation.LEFTBOTTOM, Style.SHAPE_PIN);
|
||||
}
|
||||
|
||||
graphic.drawPolygon(new Polygon(true)
|
||||
.add(2, 0)
|
||||
.add(SIZE - 2, 0)
|
||||
.add(SIZE - 2, length)
|
||||
.add(2, length), Style.NORMAL);
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package de.neemann.digital;
|
||||
import de.neemann.digital.core.Model;
|
||||
import de.neemann.digital.core.NodeException;
|
||||
import de.neemann.digital.core.ObservableValue;
|
||||
import de.neemann.digital.core.element.Element;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@ -15,13 +16,17 @@ public class TestExecuter {
|
||||
private ObservableValue[] inputs;
|
||||
private ObservableValue[] outputs;
|
||||
|
||||
public TestExecuter() throws NodeException {
|
||||
this(null);
|
||||
}
|
||||
public TestExecuter(Model model) throws NodeException {
|
||||
this(model, false);
|
||||
}
|
||||
|
||||
public TestExecuter(Model model, boolean noise) throws NodeException {
|
||||
this.model = model;
|
||||
model.init(noise);
|
||||
if (model != null)
|
||||
model.init(noise);
|
||||
}
|
||||
|
||||
public TestExecuter setInputs(ObservableValue... values) {
|
||||
@ -34,11 +39,17 @@ public class TestExecuter {
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestExecuter setOutputsOf(Element element) {
|
||||
outputs = element.getOutputs();
|
||||
return this;
|
||||
}
|
||||
|
||||
public void check(int... val) throws NodeException {
|
||||
for (int i = 0; i < inputs.length; i++) {
|
||||
inputs[i].setValue(val[i]);
|
||||
}
|
||||
model.doStep();
|
||||
if (model != null)
|
||||
model.doStep();
|
||||
|
||||
for (int i = 0; i < outputs.length; i++) {
|
||||
int should = val[i + inputs.length];
|
||||
|
102
src/test/java/de/neemann/digital/core/wiring/SplitterTest.java
Normal file
102
src/test/java/de/neemann/digital/core/wiring/SplitterTest.java
Normal file
@ -0,0 +1,102 @@
|
||||
package de.neemann.digital.core.wiring;
|
||||
|
||||
import de.neemann.digital.TestExecuter;
|
||||
import de.neemann.digital.core.ObservableValue;
|
||||
import de.neemann.digital.core.element.AttributeKey;
|
||||
import de.neemann.digital.core.element.ElementAttributes;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* @author hneemann
|
||||
*/
|
||||
public class SplitterTest extends TestCase {
|
||||
|
||||
public void testBits() throws Exception {
|
||||
ObservableValue a = new ObservableValue("a", 1);
|
||||
ObservableValue b = new ObservableValue("b", 1);
|
||||
ObservableValue c = new ObservableValue("c", 1);
|
||||
ObservableValue d = new ObservableValue("d", 1);
|
||||
|
||||
Splitter splitter = new Splitter(new ElementAttributes()
|
||||
.set(AttributeKey.InputSplit, "1,1,1,1")
|
||||
.set(AttributeKey.OutputSplit, "4"));
|
||||
|
||||
splitter.setInputs(a, b, c, d);
|
||||
|
||||
ObservableValue[] outputs = splitter.getOutputs();
|
||||
assertEquals(1, outputs.length);
|
||||
|
||||
TestExecuter sc = new TestExecuter().setInputs(a, b, c, d).setOutputsOf(splitter);
|
||||
sc.check(0, 0, 0, 0, 0);
|
||||
sc.check(1, 0, 0, 0, 1);
|
||||
sc.check(0, 1, 0, 0, 2);
|
||||
sc.check(0, 0, 1, 0, 4);
|
||||
sc.check(0, 0, 0, 1, 8);
|
||||
sc.check(1, 1, 0, 0, 3);
|
||||
sc.check(1, 1, 1, 0, 7);
|
||||
sc.check(1, 1, 1, 1, 15);
|
||||
sc.check(0, 0, 1, 1, 12);
|
||||
sc.check(0, 1, 1, 1, 14);
|
||||
}
|
||||
|
||||
public void testMoreBits() throws Exception {
|
||||
ObservableValue a = new ObservableValue("a", 4);
|
||||
ObservableValue b = new ObservableValue("b", 4);
|
||||
ObservableValue c = new ObservableValue("c", 4);
|
||||
ObservableValue d = new ObservableValue("d", 4);
|
||||
|
||||
Splitter splitter = new Splitter(new ElementAttributes()
|
||||
.set(AttributeKey.InputSplit, "4,4,4,4")
|
||||
.set(AttributeKey.OutputSplit, "16"));
|
||||
|
||||
splitter.setInputs(a, b, c, d);
|
||||
|
||||
ObservableValue[] outputs = splitter.getOutputs();
|
||||
assertEquals(1, outputs.length);
|
||||
|
||||
TestExecuter sc = new TestExecuter().setInputs(d, c, b, a).setOutputsOf(splitter);
|
||||
sc.check(0, 0, 0, 0, 0x0000);
|
||||
sc.check(0, 0, 0, 1, 0x0001);
|
||||
sc.check(0, 0, 1, 0, 0x0010);
|
||||
sc.check(0, 1, 0, 0, 0x0100);
|
||||
sc.check(1, 0, 0, 0, 0x1000);
|
||||
sc.check(0, 0, 0, 15, 0x000F);
|
||||
sc.check(0, 0, 15, 0, 0x00F0);
|
||||
sc.check(0, 15, 0, 0, 0x0F00);
|
||||
sc.check(15, 0, 0, 0, 0xF000);
|
||||
}
|
||||
|
||||
public void testMoreBits2() throws Exception {
|
||||
ObservableValue a = new ObservableValue("a", 4);
|
||||
ObservableValue b = new ObservableValue("b", 4);
|
||||
ObservableValue c = new ObservableValue("c", 4);
|
||||
ObservableValue d = new ObservableValue("d", 4);
|
||||
|
||||
Splitter splitter = new Splitter(new ElementAttributes()
|
||||
.set(AttributeKey.InputSplit, "4,4,4,4")
|
||||
.set(AttributeKey.OutputSplit, "8,8"));
|
||||
|
||||
splitter.setInputs(a, b, c, d);
|
||||
|
||||
ObservableValue[] outputs = splitter.getOutputs();
|
||||
assertEquals(2, outputs.length);
|
||||
|
||||
TestExecuter sc = new TestExecuter().setInputs(b, a, d, c).setOutputsOf(splitter);
|
||||
sc.check(0x0, 0x0, 0x0, 0x0, 0x00, 0x00);
|
||||
sc.check(0x1, 0x0, 0x0, 0x0, 0x10, 0x00);
|
||||
sc.check(0x0, 0x1, 0x0, 0x0, 0x01, 0x00);
|
||||
sc.check(0x0, 0x0, 0x1, 0x0, 0x00, 0x10);
|
||||
sc.check(0x0, 0x0, 0x0, 0x1, 0x00, 0x01);
|
||||
sc.check(0xf, 0x0, 0x0, 0x0, 0xf0, 0x00);
|
||||
sc.check(0x0, 0xf, 0x0, 0x0, 0x0f, 0x00);
|
||||
sc.check(0x0, 0x0, 0xf, 0x0, 0x00, 0xf0);
|
||||
sc.check(0x0, 0x0, 0x0, 0xf, 0x00, 0x0f);
|
||||
sc.check(0x1, 0x1, 0x0, 0x0, 0x11, 0x00);
|
||||
sc.check(0x0, 0x0, 0x1, 0x1, 0x00, 0x11);
|
||||
sc.check(0xf, 0xf, 0x0, 0x0, 0xff, 0x00);
|
||||
sc.check(0x0, 0x0, 0xf, 0xf, 0x00, 0xff);
|
||||
sc.check(0xf, 0xf, 0xf, 0xf, 0xff, 0xff);
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user