Restructured test classes and improved readability
authorChristian Femers <femers@in.tum.de>
Fri, 23 Aug 2019 02:38:40 +0000 (04:38 +0200)
committerChristian Femers <femers@in.tum.de>
Fri, 23 Aug 2019 02:38:40 +0000 (04:38 +0200)
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/Am2901Test.java [deleted file]
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/Am2901Testbench.java [deleted file]
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestEnvironmentHelper.java [new file with mode: 0644]
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestUtil.java
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableAm2901.java [deleted file]
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableAm2901Impl.java [deleted file]
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableCircuit.java [new file with mode: 0644]
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/Am2901Test.java [new file with mode: 0644]
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/Am2901Testbench.java [new file with mode: 0644]
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901.java [new file with mode: 0644]
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901Impl.java [new file with mode: 0644]

diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/Am2901Test.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/Am2901Test.java
deleted file mode 100644 (file)
index 7d87f39..0000000
+++ /dev/null
@@ -1,298 +0,0 @@
-package net.mograsim.logic.model.am2900;
-
-import static net.mograsim.logic.model.am2900.TestUtil.*;
-import static net.mograsim.logic.model.am2900.TestableAm2901.Am2901_Dest.*;
-import static net.mograsim.logic.model.am2900.TestableAm2901.Am2901_Func.*;
-import static net.mograsim.logic.model.am2900.TestableAm2901.Am2901_Src.*;
-import static net.mograsim.logic.model.am2900.TestableAm2901.Register.*;
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.awt.Point;
-import java.util.stream.IntStream;
-import java.util.stream.Stream;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Order;
-import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestMethodOrder;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.EnumSource;
-
-import net.mograsim.logic.model.am2900.TestableAm2901.Register;
-
-@DisplayName("Am2901 Tests")
-@TestMethodOrder(OrderAnnotation.class)
-public class Am2901Test
-{
-       private TestableAm2901 am2901;
-
-       @BeforeEach
-       void initialize()
-       {
-               createAndSetup();
-               setInputsToZero();
-       }
-
-       void createAndSetup()
-       {
-               am2901 = new TestableAm2901Impl();
-               am2901.setup();
-       }
-
-       void setRegistersToZero()
-       {
-               setInputsToZero();
-               for (Register r : Register.values())
-               {
-                       setRegisterToZero(r);
-               }
-       }
-
-       void setRegisterToZero(Register r)
-       {
-               am2901.setD("0000");
-               am2901.setSrc(DZ);
-               am2901.setFunc(AND);
-               setRegOutput(r);
-
-               am2901.assertFullCycleSuccess();
-       }
-
-       void setRegOutput(Register r)
-       {
-               if (r == Q)
-               {
-                       am2901.setDest(QREG);
-               } else
-               {
-                       am2901.setReg_B(r.toBitString());
-                       am2901.setDest(RAMF);
-               }
-       }
-
-       void setInputsToZero()
-       {
-               am2901.setCarryIn("0");
-               am2901.setQ_0("0");
-               am2901.setQ_3("0");
-               am2901.setRAM_0("0");
-               am2901.setRAM_3("0");
-               am2901.setReg_A("0000");
-               am2901.setReg_B("0000");
-               am2901.setD("0000");
-               am2901.setSrc(AB);
-               am2901.setFunc(ADD);
-               am2901.setDest(QREG);
-//             am2901.setNotOutEnable("0"); TODO
-               am2901.clockOn(true);
-               am2901.assertRunSuccess();
-       }
-
-       @ParameterizedTest(name = "{0}")
-       @Order(1)
-       @DisplayName("Direct / high level access")
-       @EnumSource(Register.class)
-       void testDirectAccess(Register r)
-       {
-               assertEquals("UUUU", am2901.getDirectly(r));
-
-               am2901.setDirectly(r, "1011");
-
-               assertEquals("1011", am2901.getDirectly(r));
-       }
-
-       @ParameterizedTest(name = "{0}")
-       @Order(2)
-       @DisplayName("Setting each register to 0")
-       @EnumSource(Register.class)
-       void testSetToZero(Register r)
-       {
-               assertEquals("UUUU", am2901.getDirectly(r));
-
-               setRegisterToZero(r);
-
-               assertEquals("0000", am2901.getDirectly(r));
-               assertEquals("0000", am2901.getY());
-               assertEquals("0", am2901.getCarryOut());
-               assertEquals("0", am2901.getOverflow());
-               assertEquals("0", am2901.getSign());
-               assertEquals("1", am2901.getZero());
-       }
-
-       @Test
-       @Order(3)
-       @DisplayName("Setting all registers to 0")
-       void testSetAllToZero()
-       {
-               setRegistersToZero();
-
-               assertEquals("0000", am2901.getY());
-               assertEquals("0", am2901.getCarryOut());
-               assertEquals("0", am2901.getOverflow());
-               assertEquals("0", am2901.getSign());
-               assertEquals("1", am2901.getZero());
-               assertEquals("0", am2901.getQ_0());
-               assertEquals("0", am2901.getQ_3());
-               assertEquals("0", am2901.getRAM_0());
-               assertEquals("0", am2901.getRAM_3());
-
-               assertAll("register values", Register.stream().map(r -> () ->
-               {
-                       assertEquals("0000", am2901.getDirectly(r), r.name());
-               }));
-       }
-
-       @Test
-       @Order(4)
-       @DisplayName("ADD operation")
-       void testADD()
-       {
-               am2901.setSrc(DA);
-               am2901.setFunc(ADD);
-               am2901.setDest(NOP);
-               am2901.setReg_A(r0.toBitString());
-
-               assertAll(getAll4BitPairs().map(xy -> () ->
-               {
-                       am2901.setDirectly(r0, to4bitBin(xy.x));
-                       am2901.setD(to4bitBin(xy.y));
-
-                       am2901.assertFullCycleSuccess();
-
-                       int res32Bit = xy.x + xy.y;
-                       int res4Bit = res32Bit & 0b1111;
-                       int res32Bit_sgn = signed4ToSigned32(xy.x) + signed4ToSigned32(xy.y);
-                       int res4Bit_sgn = signed4ToSigned32(res32Bit_sgn);
-
-                       assertAll("Result of " + xy.x + " + " + xy.y + " = " + res32Bit,
-                                       () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), "    Y"),
-                                       () -> assertEquals(to1bitBin(res4Bit == 0), am2901.getZero(), "    F=0"),
-                                       () -> assertEquals(to1bitBin(res4Bit & 0b1000), am2901.getSign(), "    F3"),
-                                       () -> assertEquals(to1bitBin(res32Bit > 15), am2901.getCarryOut(), "    Cn+4"),
-                                       () -> assertEquals(to1bitBin(res4Bit_sgn != res32Bit_sgn), am2901.getOverflow(), "    OVR"));
-               }));
-       }
-
-       @Test
-       @Order(4)
-       @DisplayName("AND operation")
-       void testAND()
-       {
-               am2901.setSrc(DA);
-               am2901.setFunc(AND);
-               am2901.setDest(NOP);
-               am2901.setReg_A(r0.toBitString());
-
-               assertAll(getAll4BitPairs().map(xy -> () ->
-               {
-                       am2901.setDirectly(r0, to4bitBin(xy.x));
-                       am2901.setD(to4bitBin(xy.y));
-
-                       am2901.assertFullCycleSuccess();
-
-                       int res32Bit = xy.x & xy.y;
-
-                       assertAll("Result of " + xy.x + " & " + xy.y + " = " + res32Bit,
-                                       () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), "    Y"),
-                                       () -> assertEquals(to1bitBin(res32Bit == 0), am2901.getZero(), "    F=0"),
-                                       () -> assertEquals(to1bitBin(res32Bit & 0b1000), am2901.getSign(), "    F3")
-//                                     () -> assertEquals(to1bitBin(res32Bit), am2901.getCarryOut(), "    Cn+4"), // TODO
-//                                     () -> assertEquals(to1bitBin(res32Bit), am2901.getOverflow(), "    OVR") // TODO
-                       );
-               }));
-       }
-
-       @Test
-       @Order(4)
-       @DisplayName("OR operation")
-       void testOR()
-       {
-               am2901.setSrc(DA);
-               am2901.setFunc(OR);
-               am2901.setDest(NOP);
-               am2901.setReg_A(r0.toBitString());
-
-               assertAll(getAll4BitPairs().map(xy -> () ->
-               {
-                       am2901.setDirectly(r0, to4bitBin(xy.x));
-                       am2901.setD(to4bitBin(xy.y));
-
-                       am2901.assertFullCycleSuccess();
-
-                       int res32Bit = xy.x | xy.y;
-
-                       assertAll("Result of " + xy.x + " | " + xy.y + " = " + res32Bit,
-                                       () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), "    Y"),
-                                       () -> assertEquals(to1bitBin(res32Bit == 0), am2901.getZero(), "    F=0"),
-                                       () -> assertEquals(to1bitBin(res32Bit & 0b1000), am2901.getSign(), "    F3")
-//                                     () -> assertEquals(to1bitBin(res32Bit != 0b1111), am2901.getCarryOut(), "    Cn+4"), // TODO
-//                                     () -> assertEquals(to1bitBin(res32Bit != 0b1111), am2901.getOverflow(), "    OVR") // TODO
-                       );
-               }));
-       }
-
-       @Test
-       @Order(4)
-       @DisplayName("XOR operation")
-       void testXOR()
-       {
-               am2901.setSrc(DA);
-               am2901.setFunc(EXOR);
-               am2901.setDest(NOP);
-               am2901.setReg_A(r0.toBitString());
-
-               assertAll(getAll4BitPairs().map(xy -> () ->
-               {
-                       am2901.setDirectly(r0, to4bitBin(xy.x));
-                       am2901.setD(to4bitBin(xy.y));
-
-                       am2901.assertFullCycleSuccess();
-
-                       int res32Bit = xy.x ^ xy.y;
-
-                       assertAll("Result of " + xy.x + " ^ " + xy.y + " = " + res32Bit,
-                                       () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), "    Y"),
-                                       () -> assertEquals(to1bitBin(res32Bit == 0), am2901.getZero(), "    F=0"),
-                                       () -> assertEquals(to1bitBin(res32Bit & 0b1000), am2901.getSign(), "    F3"));
-               }));
-       }
-
-       @Test
-       @Order(4)
-       @DisplayName("SUB operation")
-       void testSUB()
-       {
-               am2901.setSrc(DA);
-               am2901.setCarryIn("1");
-               am2901.setFunc(SUBR);
-               am2901.setDest(NOP);
-               am2901.setReg_A(r0.toBitString());
-
-               assertAll(getAll4BitPairs().map(xy -> () ->
-               {
-                       am2901.setDirectly(r0, to4bitBin(xy.x));
-                       am2901.setD(to4bitBin(xy.y));
-
-                       am2901.assertFullCycleSuccess();
-
-                       int res32Bit = xy.x - xy.y;
-                       int res4Bit = res32Bit & 0b1111;
-                       int res32Bit_sgn = signed4ToSigned32(xy.x) - signed4ToSigned32(xy.y);
-                       int res4Bit_sgn = signed4ToSigned32(res32Bit_sgn);
-
-                       assertAll("Result of " + xy.x + " - " + xy.y + " = " + res32Bit,
-                                       () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), "    Y"),
-                                       () -> assertEquals(to1bitBin(res4Bit == 0), am2901.getZero(), "    F=0"),
-                                       () -> assertEquals(to1bitBin(res4Bit & 0b1000), am2901.getSign(), "    F3"),
-                                       () -> assertEquals(to1bitBin(xy.x >= xy.y), am2901.getCarryOut(), "    Cn+4"),
-                                       () -> assertEquals(to1bitBin(res4Bit_sgn != res32Bit_sgn), am2901.getOverflow(), "    OVR"));
-               }));
-       }
-
-       static Stream<Point> getAll4BitPairs()
-       {
-               return IntStream.range(0, 16).boxed().flatMap(x -> IntStream.range(0, 16).mapToObj(y -> new Point(x, y)));
-       }
-}
diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/Am2901Testbench.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/Am2901Testbench.java
deleted file mode 100644 (file)
index 731147c..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-package net.mograsim.logic.model.am2900;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-
-import net.mograsim.logic.model.SimpleLogicUIStandalone;
-import net.mograsim.logic.model.model.ViewModelModifiable;
-import net.mograsim.logic.model.model.components.GUIComponent;
-import net.mograsim.logic.model.model.components.atomic.GUIAndGate;
-import net.mograsim.logic.model.model.components.atomic.GUIBitDisplay;
-import net.mograsim.logic.model.model.components.atomic.GUIManualSwitch;
-import net.mograsim.logic.model.model.components.atomic.GUINotGate;
-import net.mograsim.logic.model.model.components.atomic.TextComponent;
-import net.mograsim.logic.model.model.wires.Pin;
-import net.mograsim.logic.model.model.wires.WireCrossPoint;
-import net.mograsim.logic.model.serializing.IndirectGUIComponentCreator;
-import net.mograsim.logic.model.util.ModellingTool;
-
-public class Am2901Testbench
-{
-       public static void main(String[] args)
-       {
-               SimpleLogicUIStandalone.executeVisualisation(Am2901Testbench::createTestbench);
-       }
-
-       public static void createTestbench(ViewModelModifiable model)
-       {
-               GUIComponent comp = IndirectGUIComponentCreator.createComponent(model, "GUIAm2901");
-               ModellingTool tool = ModellingTool.createFor(model);
-
-               comp.moveTo(240, 0);
-
-               GUIManualSwitch enable = new GUIManualSwitch(model, 1);
-               WireCrossPoint wcp0 = new WireCrossPoint(model, 1);
-               GUINotGate not1 = new GUINotGate(model, 1);
-               GUINotGate not2 = new GUINotGate(model, 1);
-               GUINotGate not3 = new GUINotGate(model, 1);
-               GUIAndGate and = new GUIAndGate(model, 1);
-               tool.connect(wcp0, enable, "");
-               tool.connect(wcp0, and, "A");
-               tool.connect(wcp0, not1, "A");
-               tool.connect(not1, not2, "Y", "A");
-               tool.connect(not2, not3, "Y", "A");
-               tool.connect(not3, and, "Y", "B");
-               enable.moveTo(20, -32.5);
-               wcp0.moveTo(35, -26);
-               not1.moveTo(50, -20);
-               not2.moveTo(80, -20);
-               not3.moveTo(110, -20);
-               and.moveTo(135, -30);
-               Pin last = and.getPin("Y");
-
-               // guess which pins are outputs and which are inputs
-               // TODO this code exists four times... but it seems too "hacky" to put it in a helper class
-               List<String> inputPinNames = new ArrayList<>();
-               List<String> outputPinNames = new ArrayList<>();
-               for (Pin p : comp.getPins().values())
-                       if (p.getRelX() == 0)
-                               inputPinNames.add(p.name);
-                       else
-                               outputPinNames.add(p.name);
-
-               inputPinNames.sort(Comparator.comparing(comp::getPin, Comparator.comparing(Pin::getRelY)));
-               outputPinNames.sort(Comparator.comparing(comp::getPin, Comparator.comparing(Pin::getRelY)));
-
-               for (int i = 0; i < inputPinNames.size(); i++)
-               {
-                       double x = 55 + 70 * (i % 2);
-                       double y = 10 * i;
-
-                       WireCrossPoint wcp = new WireCrossPoint(model, 1);
-                       GUIComponent d_ff = IndirectGUIComponentCreator.createComponent(model, "GUIdff");
-                       GUIManualSwitch sw = new GUIManualSwitch(model, 1);
-
-                       tool.connect(last, wcp);
-                       tool.connect(wcp, d_ff, "C");
-                       tool.connect(sw, d_ff, "", "D");
-                       tool.connect(d_ff, comp, "Q", inputPinNames.get(i));
-                       last = wcp.getPin();
-
-                       TextComponent label = new TextComponent(model, inputPinNames.get(i));
-
-                       sw.moveTo(x, y + 7.5);
-                       wcp.moveTo(160, y);
-                       d_ff.moveTo(170, y);
-                       label.moveTo(x - 48, y + 8);
-               }
-
-               for (int i = 0; i < outputPinNames.size(); i++)
-               {
-                       double x = 300 + 75 * (i % 2);
-                       double y = 10 * i - 2.5;
-                       GUIBitDisplay bd = new GUIBitDisplay(model, 1);
-                       bd.moveTo(x, y);
-                       tool.connect(bd.getInputPin(), comp, outputPinNames.get(i));
-
-                       TextComponent label = new TextComponent(model, outputPinNames.get(i));
-                       label.moveTo(x + 25, y);
-               }
-       }
-}
\ No newline at end of file
diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestEnvironmentHelper.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestEnvironmentHelper.java
new file mode 100644 (file)
index 0000000..0a4838f
--- /dev/null
@@ -0,0 +1,253 @@
+package net.mograsim.logic.model.am2900;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.Set;
+import java.util.TreeSet;
+
+import net.mograsim.logic.core.components.BitDisplay;
+import net.mograsim.logic.core.components.ManualSwitch;
+import net.mograsim.logic.core.timeline.Timeline;
+import net.mograsim.logic.model.am2900.TestableCircuit.Result;
+import net.mograsim.logic.model.model.ViewModel;
+import net.mograsim.logic.model.model.ViewModelModifiable;
+import net.mograsim.logic.model.model.components.GUIComponent;
+import net.mograsim.logic.model.model.components.atomic.GUIBitDisplay;
+import net.mograsim.logic.model.model.components.atomic.GUIManualSwitch;
+import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
+import net.mograsim.logic.model.model.wires.GUIWire;
+import net.mograsim.logic.model.model.wires.Pin;
+import net.mograsim.logic.model.modeladapter.LogicModelParameters;
+import net.mograsim.logic.model.modeladapter.ViewLogicModelAdapter;
+import net.mograsim.logic.model.serializing.IndirectGUIComponentCreator;
+import net.mograsim.logic.model.util.ModellingTool;
+
+public class TestEnvironmentHelper
+{
+       private final TestableCircuit testEnvInstance;
+       private final Class<?> testEnvClass;
+       private final String modelId;
+       private Field componentField;
+       private Field timelineField;
+
+       private GUIComponent component;
+       private Timeline timeline;
+       private ViewModelModifiable viewModel;
+       private ModellingTool modellingTool;
+       private HashMap<String, GUIManualSwitch> idSwitchMap = new HashMap<>();
+       private HashMap<String, GUIBitDisplay> idDisplayMap = new HashMap<>();
+
+       private DebugState debug = DebugState.NO_DEBUG;
+       private Set<String> wireDebugChangeSet;
+       private boolean debugWires = false;
+       public int debugEventThreshold = 10_000;
+       public int debugEventCount = 500;
+       private int eventCounter;
+
+       public TestEnvironmentHelper(TestableCircuit testEnv, String modelId)
+       {
+               this.testEnvInstance = testEnv;
+               this.modelId = modelId;
+               this.testEnvClass = testEnvInstance.getClass();
+               for (Field f : testEnvClass.getDeclaredFields())
+               {
+                       if (GUIComponent.class.isAssignableFrom(f.getType()))
+                       {
+                               componentField = f;
+                               componentField.setAccessible(true);
+                       } else if (Timeline.class.isAssignableFrom(f.getType()))
+                       {
+                               timelineField = f;
+                               timelineField.setAccessible(true);
+                       }
+               }
+               if (componentField == null || timelineField == null)
+                       throw new IllegalStateException("No component or timeline field found!");
+       }
+
+       public void setup(DebugState debug)
+       {
+               this.debug = debug;
+               // Create view model
+               viewModel = new ViewModelModifiable();
+               modellingTool = ModellingTool.createFor(viewModel);
+               component = IndirectGUIComponentCreator.createComponent(viewModel, modelId);
+               setField(componentField, component);
+
+               component.getPins().values().forEach(this::extendModelPin);
+
+               // Create logic model
+               LogicModelParameters params = new LogicModelParameters();
+               params.gateProcessTime = 50;
+               params.wireTravelTime = 10;
+               timeline = ViewLogicModelAdapter.convert(viewModel, params);
+               setField(timelineField, timeline);
+
+               // Bind switches/displays to this test class
+               component.getPins().values().forEach(this::bindModelPin);
+
+               if (debug == DebugState.DEBUG_AT_PERFORMANCE_COST)
+               {
+                       setupDebugging();
+               }
+               timeline.addEventAddedListener(te -> eventCounter++);
+       }
+
+       private void extendModelPin(Pin p)
+       {
+               String javaIdentId = idToJavaIdentifier(p.name);
+               try
+               {
+                       Field f = testEnvClass.getDeclaredField(javaIdentId);
+                       Class<?> type = f.getType();
+                       if (ManualSwitch.class.isAssignableFrom(type))
+                       {
+                               GUIManualSwitch gms = new GUIManualSwitch(viewModel, p.logicWidth);
+                               modellingTool.connect(p, gms.getOutputPin());
+                               idSwitchMap.put(p.name, gms);
+                       } else if (BitDisplay.class.isAssignableFrom(type))
+                       {
+                               GUIBitDisplay gbd = new GUIBitDisplay(viewModel, p.logicWidth);
+                               modellingTool.connect(p, gbd.getInputPin());
+                               idDisplayMap.put(p.name, gbd);
+                       } else
+                       {
+                               fail("unkown field type " + type);
+                       }
+               }
+               catch (NoSuchFieldException | SecurityException e)
+               {
+                       fail(e);
+               }
+       }
+
+       private void bindModelPin(Pin p)
+       {
+               String javaIdentId = idToJavaIdentifier(p.name);
+               if (idDisplayMap.containsKey(p.name))
+                       setField(javaIdentId, idDisplayMap.get(p.name).getBitDisplay());
+               if (idSwitchMap.containsKey(p.name))
+                       setField(javaIdentId, idSwitchMap.get(p.name).getManualSwitch());
+       }
+
+       private void setupDebugging()
+       {
+               // Debug code
+               HashSet<GUIWire> wiresIncludingSubmodels = new HashSet<>();
+               Queue<ViewModel> modelsToIterate = new LinkedList<>();
+               modelsToIterate.add(viewModel);
+               while (modelsToIterate.size() > 0)
+               {
+                       ViewModel model = modelsToIterate.poll();
+                       wiresIncludingSubmodels.addAll(model.getWiresByName().values());
+                       for (GUIComponent comp : model.getComponentsByName().values())
+                               if (comp instanceof SubmodelComponent)
+                                       modelsToIterate.offer(((SubmodelComponent) comp).submodel);
+               }
+               System.out.println(wiresIncludingSubmodels.size());
+               viewModel.setRedrawHandler(() -> wiresIncludingSubmodels.forEach(w ->
+               {
+                       if (debugWires)
+                       {
+                               wireDebugChangeSet.add(w.toString());
+                       }
+               }));
+       }
+
+       public Result run()
+       {
+               // Normal execution until completion or eventLimit
+               int eventLimit = debugEventThreshold;
+               eventCounter = 0;
+               debugWires = false;
+               while (eventCounter < eventLimit)
+               {
+                       timeline.executeNext();
+                       if (!timeline.hasNext())
+                               return Result.SUCCESS;
+               }
+
+               // Start debugging if event limit is reached (if debug is active)
+               if (debug == DebugState.DEBUG_AT_PERFORMANCE_COST)
+                       return debugThisRun();
+
+               return Result.OUT_OF_TIME;
+       }
+
+       private Result debugThisRun()
+       {
+               int eventLimit = debugEventThreshold;
+               debugWires = true;
+               wireDebugChangeSet = new TreeSet<>();
+               Set<String> oldChangeSet;
+               // observe wire changes to detect, if we are really stuck in an endless loop
+               do
+               {
+                       eventLimit += debugEventCount;
+                       oldChangeSet = wireDebugChangeSet;
+                       wireDebugChangeSet = new TreeSet<>();
+                       while (eventCounter < eventLimit)
+                       {
+                               timeline.executeNext();
+                               if (!timeline.hasNext())
+                               {
+                                       // no endless loop, but more events needed than expected
+                                       System.out.println("run() took longer than expected: " + eventCounter);
+                                       return Result.SUCCESS;
+                               }
+                       }
+               } while (!oldChangeSet.equals(wireDebugChangeSet));
+               // if stuck, abort execution and print wires
+               System.err.print("Problematic Wire updates:");
+               wireDebugChangeSet.forEach(System.out::println);
+               System.err.println("run() failed: " + eventCounter);
+               return Result.OUT_OF_TIME;
+       }
+
+       private static String idToJavaIdentifier(String s)
+       {
+               StringBuilder sb = new StringBuilder(s.length());
+               char c = s.charAt(0);
+               sb.append(Character.isJavaIdentifierStart(c) ? c : '_');
+               for (int i = 1; i < s.length(); i++)
+                       sb.append(Character.isJavaIdentifierPart(c = s.charAt(i)) ? c : '_');
+               return sb.toString();
+       }
+
+       private <S> void setField(Field f, S value)
+       {
+               try
+               {
+                       f.set(testEnvInstance, Objects.requireNonNull(value));
+               }
+               catch (Exception e)
+               {
+                       fail(e);
+               }
+       }
+
+       private <S> void setField(String name, S value)
+       {
+               try
+               {
+                       Field f = testEnvClass.getDeclaredField(name);
+                       f.setAccessible(true);
+                       f.set(testEnvInstance, Objects.requireNonNull(value));
+               }
+               catch (Exception e)
+               {
+                       fail(e);
+               }
+       }
+
+       public enum DebugState
+       {
+               NO_DEBUG, DEBUG_AT_PERFORMANCE_COST;
+       }
+}
index f85ae04..46a4b92 100644 (file)
@@ -1,5 +1,15 @@
 package net.mograsim.logic.model.am2900;
 
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.lang.reflect.Field;
+import java.util.Objects;
+
+import net.mograsim.logic.core.types.Bit;
+import net.mograsim.logic.core.types.BitVector;
+import net.mograsim.logic.core.types.BitVector.BitVectorMutator;
+import net.mograsim.logic.model.am2900.am2901.TestableAm2901Impl;
+
 public final class TestUtil
 {
        private TestUtil()
@@ -81,4 +91,16 @@ public final class TestUtil
                }
                return sb.reverse().toString();
        }
+
+       public static BitVector of(int value, int length)
+       {
+               BitVectorMutator mutator = BitVectorMutator.ofLength(length);
+               int val = value;
+               for (int i = length - 1; i >= 0; i--)
+               {
+                       mutator.setMSBit(i, Bit.lastBitOf(val));
+                       val >>>= 1;
+               }
+               return mutator.toBitVector();
+       }
 }
diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableAm2901.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableAm2901.java
deleted file mode 100644 (file)
index d898b39..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-package net.mograsim.logic.model.am2900;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import java.util.Arrays;
-import java.util.stream.Stream;
-
-public interface TestableAm2901
-{
-       void setup();
-
-       Result run();
-
-       void setDest(Am2901_Dest dest);
-
-       void setFunc(Am2901_Func func);
-
-       void setSrc(Am2901_Src src);
-
-       void setReg_A(String val_4_bit);
-
-       void setReg_B(String val_4_bit);
-
-       void setCarryIn(String val_1_bit);
-
-       void setNotOutEnable(String val_1_bit);
-
-       void setD(String val_4_bit);
-
-       void setQ_0(String val_1_bit);
-
-       void setQ_3(String val_1_bit);
-
-       void setRAM_0(String val_1_bit);
-
-       void setRAM_3(String val_1_bit);
-
-       void clockOn(boolean isClockOn);
-
-       void setDirectly(Register r, String val_4_bit);
-
-       String getQ_0();
-
-       String getQ_3();
-
-       String getRAM_0();
-
-       String getRAM_3();
-
-       String getNotP();
-
-       String getNotG();
-
-       String getCarryOut();
-
-       String getSign();
-
-       String getZero();
-
-       String getOverflow();
-
-       String getY();
-
-       String getDirectly(Register r);
-
-       default void assertRunSuccess()
-       {
-               assertEquals(Result.SUCCESS, run());
-       }
-
-       default void assertFullCycleSuccess()
-       {
-               assertRunSuccess();
-               clockOn(false);
-               assertRunSuccess();
-               clockOn(true);
-               assertRunSuccess();
-       }
-
-       public enum Result
-       {
-               SUCCESS, OUT_OF_TIME, ERROR;
-       }
-
-       public enum Am2901_Dest
-       {
-               QREG, NOP, RAMA, RAMF, RAMQD, RAMD, RAMQU, RAMU;
-
-               public boolean doesShift()
-               {
-                       return ordinal() >= 4;
-               }
-
-               public int getShiftDir()
-               {
-                       return doesShift() ? (ordinal() < 6 ? -1 : 1) : 0;
-               }
-
-               public int getI7()
-               {
-                       return this.ordinal() >> 1 & 1;
-               }
-       }
-
-       public enum Am2901_Func
-       {
-               ADD, SUBR, SUBS, OR, AND, NOTRS, EXOR, EXNOR;
-       }
-
-       public enum Am2901_Src
-       {
-               AQ, AB, ZQ, ZB, ZA, DA, DQ, DZ;
-       }
-
-       public enum Register
-       {
-               r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, rA, rB, rC, rD, rE, rF, Q;
-
-               public String toBitString()
-               {
-                       if (this.ordinal() > 0xF)
-                               throw new UnsupportedOperationException();
-                       return TestUtil.to4bitBin(this.ordinal());
-               }
-
-               public static Stream<Register> stream()
-               {
-                       return Arrays.stream(values());
-               }
-       }
-}
diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableAm2901Impl.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableAm2901Impl.java
deleted file mode 100644 (file)
index e84588d..0000000
+++ /dev/null
@@ -1,381 +0,0 @@
-package net.mograsim.logic.model.am2900;
-
-import static org.junit.jupiter.api.Assertions.fail;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Objects;
-import java.util.Queue;
-import java.util.Set;
-import java.util.TreeSet;
-
-import net.mograsim.logic.core.components.BitDisplay;
-import net.mograsim.logic.core.components.ManualSwitch;
-import net.mograsim.logic.core.timeline.Timeline;
-import net.mograsim.logic.core.types.Bit;
-import net.mograsim.logic.core.types.BitVector;
-import net.mograsim.logic.core.types.BitVector.BitVectorMutator;
-import net.mograsim.logic.model.model.ViewModel;
-import net.mograsim.logic.model.model.ViewModelModifiable;
-import net.mograsim.logic.model.model.components.GUIComponent;
-import net.mograsim.logic.model.model.components.atomic.GUIBitDisplay;
-import net.mograsim.logic.model.model.components.atomic.GUIManualSwitch;
-import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
-import net.mograsim.logic.model.model.wires.GUIWire;
-import net.mograsim.logic.model.model.wires.Pin;
-import net.mograsim.logic.model.modeladapter.LogicModelParameters;
-import net.mograsim.logic.model.modeladapter.ViewLogicModelAdapter;
-import net.mograsim.logic.model.serializing.IndirectGUIComponentCreator;
-
-public class TestableAm2901Impl implements TestableAm2901
-{
-       private GUIComponent am2901;
-       private Timeline timeline;
-       private ManualSwitch I8, I7, I6, I5, I4, I3, I2, I1, I0;
-       private ManualSwitch C;
-       private ManualSwitch Cn;
-       private ManualSwitch D1, D2, D3, D4;
-       private ManualSwitch A0, A1, A2, A3;
-       private ManualSwitch B0, B1, B2, B3;
-       private ManualSwitch IRAMn, IRAMn_3, IQn, IQn_3;
-       private BitDisplay Y1, Y2, Y3, Y4;
-       private BitDisplay F_0, Cn_4, OVR, F3;
-       private BitDisplay ORAMn, ORAMn_3, OQn, OQn_3;
-
-       private Set<String> wireDebugChangeSet;
-       private boolean debugWires = false;
-       public int debugEventThreshold = 10_000;
-       public int debugEventCount = 500;
-
-       private int eventCounter;
-
-       @Override
-       public Result run()
-       {
-               // Normal execution until completion or eventLimit
-               int eventLimit = debugEventThreshold;
-               eventCounter = 0;
-               debugWires = false;
-               while (eventCounter < eventLimit)
-               {
-                       timeline.executeNext();
-                       if (!timeline.hasNext())
-                       {
-//                             System.out.println("run() took " + eventCounter + " events");
-                               return Result.SUCCESS;
-                       }
-               }
-               // Start debugging if event limit is reached
-               debugWires = true;
-               wireDebugChangeSet = new TreeSet<>();
-               Set<String> oldChangeSet;
-               // observe wire changes to detect, if we are really stuck in an endless loop
-               do
-               {
-                       eventLimit += debugEventCount;
-                       oldChangeSet = wireDebugChangeSet;
-                       wireDebugChangeSet = new TreeSet<>();
-                       while (eventCounter < eventLimit)
-                       {
-                               timeline.executeNext();
-                               if (!timeline.hasNext())
-                               {
-                                       // no endless loop, but more events needed than expected
-                                       System.out.println("run() took longer than expected: " + eventCounter);
-                                       return Result.SUCCESS;
-                               }
-                       }
-               } while (!oldChangeSet.equals(wireDebugChangeSet));
-               // if stuck, abort execution and print wires
-               System.err.print("Problematic Wire updates:");
-               wireDebugChangeSet.forEach(System.out::println);
-               System.err.println("run() failed: " + eventCounter);
-               return Result.OUT_OF_TIME;
-       }
-
-       @SuppressWarnings("unused")
-       @Override
-       public void setup()
-       {
-               // Create view model
-               ViewModelModifiable viewModel = new ViewModelModifiable();
-               am2901 = IndirectGUIComponentCreator.createComponent(viewModel, "GUIAm2901");
-               // guess which pins are outputs and which are inputs
-               // TODO this code exists four times... but it seems too "hacky" to put it in a helper class
-               List<String> inputPinNames = new ArrayList<>();
-               List<String> outputPinNames = new ArrayList<>();
-               for (Pin p : am2901.getPins().values())
-                       if (p.getRelX() == 0)
-                               inputPinNames.add(p.name);
-                       else
-                               outputPinNames.add(p.name);
-               // Get switches
-               HashMap<String, GUIManualSwitch> idSwitchMap = new HashMap<>();
-               for (String id : inputPinNames)
-               {
-                       GUIManualSwitch sw = new GUIManualSwitch(viewModel, 1);
-                       new GUIWire(viewModel, am2901.getPin(id), sw.getOutputPin());
-                       idSwitchMap.put(id, sw);
-               }
-               // Get displays
-               HashMap<String, GUIBitDisplay> idDisplayMap = new HashMap<>();
-               for (String id : outputPinNames)
-               {
-                       GUIBitDisplay bd = new GUIBitDisplay(viewModel, 1);
-//                     bd.addRedrawListener(() -> System.out.println(id + " " + bd.getBitDisplay().getDisplayedValue()));
-                       new GUIWire(viewModel, am2901.getPin(id), bd.getInputPin());
-                       idDisplayMap.put(id, bd);
-               }
-               // Create logic model
-               LogicModelParameters params = new LogicModelParameters();
-               params.gateProcessTime = 50;
-               params.wireTravelTime = 10;
-               timeline = ViewLogicModelAdapter.convert(viewModel, params);
-               // Bind switches/displays to this test class
-               for (var entry : idSwitchMap.entrySet())
-                       setField(entry.getKey().replaceAll("\\+|=", "_"), entry.getValue().getManualSwitch());
-               for (var entry : idDisplayMap.entrySet())
-                       setField(entry.getKey().replaceAll("\\+|=", "_"), entry.getValue().getBitDisplay());
-
-               // Debug code
-               HashSet<GUIWire> wiresIncludingSubmodels = new HashSet<>();
-               Queue<ViewModel> modelsToIterate = new LinkedList<>();
-               modelsToIterate.add(viewModel);
-               while (modelsToIterate.size() > 0)
-               {
-                       ViewModel model = modelsToIterate.poll();
-                       wiresIncludingSubmodels.addAll(model.getWiresByName().values());
-                       for (GUIComponent comp : model.getComponentsByName().values())
-                               if (comp instanceof SubmodelComponent)
-                                       modelsToIterate.offer(((SubmodelComponent) comp).submodel);
-               }
-               // NOTE: This commented out code is only used for serious debugging (performance cost!)
-//             System.out.println(wiresIncludingSubmodels.size());
-//             viewModel.setRedrawHandler(() -> wiresIncludingSubmodels.forEach(w ->
-//             {
-//                     if (debugWires)
-//                     {
-//                             wireDebugChangeSet.add(w.toString());
-//                     }
-//             }));
-               timeline.addEventAddedListener(te -> eventCounter++);
-       }
-
-       @Override
-       public void setDest(Am2901_Dest dest)
-       {
-               var bits = of(dest.ordinal(), 3);
-               I8.setState(bits.getLSBit(2));
-               I7.setState(bits.getLSBit(1));
-               I6.setState(bits.getLSBit(0));
-       }
-
-       @Override
-       public void setFunc(Am2901_Func func)
-       {
-               var bits = of(func.ordinal(), 3);
-               I5.setState(bits.getLSBit(2));
-               I4.setState(bits.getLSBit(1));
-               I3.setState(bits.getLSBit(0));
-       }
-
-       @Override
-       public void setSrc(Am2901_Src src)
-       {
-               var bits = of(src.ordinal(), 3);
-               I2.setState(bits.getLSBit(2));
-               I1.setState(bits.getLSBit(1));
-               I0.setState(bits.getLSBit(0));
-       }
-
-       @Override
-       public void setReg_A(String val_4_bit)
-       {
-               var bits = BitVector.parse(val_4_bit);
-               A3.setState(bits.getLSBit(3));
-               A2.setState(bits.getLSBit(2));
-               A1.setState(bits.getLSBit(1));
-               A0.setState(bits.getLSBit(0));
-       }
-
-       @Override
-       public void setReg_B(String val_4_bit)
-       {
-               var bits = BitVector.parse(val_4_bit);
-               B3.setState(bits.getLSBit(3));
-               B2.setState(bits.getLSBit(2));
-               B1.setState(bits.getLSBit(1));
-               B0.setState(bits.getLSBit(0));
-       }
-
-       @Override
-       public void setCarryIn(String val_1_bit)
-       {
-               Cn.setState(Bit.parse(val_1_bit));
-       }
-
-       @Override
-       public void setNotOutEnable(String val_1_bit)
-       {
-               throw new UnsupportedOperationException(); // TODO
-       }
-
-       @Override
-       public void setD(String val_4_bit)
-       {
-               var bits = BitVector.parse(val_4_bit);
-               D4.setState(bits.getLSBit(3));
-               D3.setState(bits.getLSBit(2));
-               D2.setState(bits.getLSBit(1));
-               D1.setState(bits.getLSBit(0));
-       }
-
-       @Override
-       public void setQ_0(String val_1_bit)
-       {
-               IQn.setState(Bit.parse(val_1_bit));
-       }
-
-       @Override
-       public void setQ_3(String val_1_bit)
-       {
-               IQn_3.setState(Bit.parse(val_1_bit));
-       }
-
-       @Override
-       public void setRAM_0(String val_1_bit)
-       {
-               IRAMn.setState(Bit.parse(val_1_bit));
-       }
-
-       @Override
-       public void setRAM_3(String val_1_bit)
-       {
-               IRAMn_3.setState(Bit.parse(val_1_bit));
-       }
-
-       @Override
-       public void clockOn(boolean isClockOn)
-       {
-               C.setState(isClockOn ? Bit.ONE : Bit.ZERO);
-       }
-
-       @Override
-       public String getQ_0()
-       {
-               return OQn.getDisplayedValue().toString();
-       }
-
-       @Override
-       public String getQ_3()
-       {
-               return OQn_3.getDisplayedValue().toString();
-       }
-
-       @Override
-       public String getRAM_0()
-       {
-               return ORAMn.getDisplayedValue().toString();
-       }
-
-       @Override
-       public String getRAM_3()
-       {
-               return ORAMn_3.getDisplayedValue().toString();
-       }
-
-       @Override
-       public String getNotP()
-       {
-               throw new UnsupportedOperationException(); // TODO
-       }
-
-       @Override
-       public String getNotG()
-       {
-               throw new UnsupportedOperationException(); // TODO
-       }
-
-       @Override
-       public String getCarryOut()
-       {
-               return Cn_4.getDisplayedValue().toString();
-       }
-
-       @Override
-       public String getSign()
-       {
-               return F3.getDisplayedValue().toString();
-       }
-
-       @Override
-       public String getZero()
-       {
-               return F_0.getDisplayedValue().toString();
-       }
-
-       @Override
-       public String getOverflow()
-       {
-               return OVR.getDisplayedValue().toString();
-       }
-
-       @Override
-       public String getY()
-       {
-               var y3 = Y4.getDisplayedValue();
-               var y2 = Y3.getDisplayedValue();
-               var y1 = Y2.getDisplayedValue();
-               var y0 = Y1.getDisplayedValue();
-               return y3.concat(y2).concat(y1).concat(y0).toString();
-       }
-
-       private void setField(String name, Object value)
-       {
-               try
-               {
-                       Field f = TestableAm2901Impl.class.getDeclaredField(name);
-                       f.setAccessible(true);
-                       f.set(this, Objects.requireNonNull(value));
-               }
-               catch (Exception e)
-               {
-                       fail(e);
-               }
-       }
-
-       private static BitVector of(int value, int length)
-       {
-               BitVectorMutator mutator = BitVectorMutator.ofLength(length);
-               int val = value;
-               for (int i = length - 1; i >= 0; i--)
-               {
-                       mutator.setMSBit(i, Bit.lastBitOf(val));
-                       val >>>= 1;
-               }
-               return mutator.toBitVector();
-       }
-
-       @Override
-       public void setDirectly(Register r, String val_4_bit)
-       {
-               am2901.setHighLevelState(regToStateID(r), BitVector.parse(val_4_bit));
-       }
-
-       @Override
-       public String getDirectly(Register r)
-       {
-               return ((BitVector) am2901.getHighLevelState(regToStateID(r))).toString();
-       }
-
-       private static String regToStateID(Register r)
-       {
-               if (r == Register.Q)
-                       return "qreg.q";
-               return "regs.c" + r.toBitString() + ".q";
-       }
-}
diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableCircuit.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableCircuit.java
new file mode 100644 (file)
index 0000000..4b1a5a6
--- /dev/null
@@ -0,0 +1,31 @@
+package net.mograsim.logic.model.am2900;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public interface TestableCircuit
+{
+       void setup();
+
+       Result run();
+
+       void clockOn(boolean isClockOn);
+
+       default void assertRunSuccess()
+       {
+               assertEquals(Result.SUCCESS, run());
+       }
+
+       default void assertFullCycleSuccess()
+       {
+               assertRunSuccess();
+               clockOn(false);
+               assertRunSuccess();
+               clockOn(true);
+               assertRunSuccess();
+       }
+
+       public enum Result
+       {
+               SUCCESS, OUT_OF_TIME, ERROR;
+       }
+}
diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/Am2901Test.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/Am2901Test.java
new file mode 100644 (file)
index 0000000..57461c6
--- /dev/null
@@ -0,0 +1,298 @@
+package net.mograsim.logic.model.am2900.am2901;
+
+import static net.mograsim.logic.model.am2900.TestUtil.*;
+import static net.mograsim.logic.model.am2900.am2901.TestableAm2901.Am2901_Dest.*;
+import static net.mograsim.logic.model.am2900.am2901.TestableAm2901.Am2901_Func.*;
+import static net.mograsim.logic.model.am2900.am2901.TestableAm2901.Am2901_Src.*;
+import static net.mograsim.logic.model.am2900.am2901.TestableAm2901.Register.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.awt.Point;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+import net.mograsim.logic.model.am2900.am2901.TestableAm2901.Register;
+
+@DisplayName("Am2901 Tests")
+@TestMethodOrder(OrderAnnotation.class)
+public class Am2901Test
+{
+       private TestableAm2901 am2901;
+
+       @BeforeEach
+       void initialize()
+       {
+               createAndSetup();
+               setInputsToZero();
+       }
+
+       void createAndSetup()
+       {
+               am2901 = new TestableAm2901Impl();
+               am2901.setup();
+       }
+
+       void setRegistersToZero()
+       {
+               setInputsToZero();
+               for (Register r : Register.values())
+               {
+                       setRegisterToZero(r);
+               }
+       }
+
+       void setRegisterToZero(Register r)
+       {
+               am2901.setD("0000");
+               am2901.setSrc(DZ);
+               am2901.setFunc(AND);
+               setRegOutput(r);
+
+               am2901.assertFullCycleSuccess();
+       }
+
+       void setRegOutput(Register r)
+       {
+               if (r == Q)
+               {
+                       am2901.setDest(QREG);
+               } else
+               {
+                       am2901.setReg_B(r.toBitString());
+                       am2901.setDest(RAMF);
+               }
+       }
+
+       void setInputsToZero()
+       {
+               am2901.setCarryIn("0");
+               am2901.setQ_0("0");
+               am2901.setQ_3("0");
+               am2901.setRAM_0("0");
+               am2901.setRAM_3("0");
+               am2901.setReg_A("0000");
+               am2901.setReg_B("0000");
+               am2901.setD("0000");
+               am2901.setSrc(AB);
+               am2901.setFunc(ADD);
+               am2901.setDest(QREG);
+//             am2901.setNotOutEnable("0"); TODO
+               am2901.clockOn(true);
+               am2901.assertRunSuccess();
+       }
+
+       @ParameterizedTest(name = "{0}")
+       @Order(1)
+       @DisplayName("Direct / high level access")
+       @EnumSource(Register.class)
+       void testDirectAccess(Register r)
+       {
+               assertEquals("UUUU", am2901.getDirectly(r));
+
+               am2901.setDirectly(r, "1011");
+
+               assertEquals("1011", am2901.getDirectly(r));
+       }
+
+       @ParameterizedTest(name = "{0}")
+       @Order(2)
+       @DisplayName("Setting each register to 0")
+       @EnumSource(Register.class)
+       void testSetToZero(Register r)
+       {
+               assertEquals("UUUU", am2901.getDirectly(r));
+
+               setRegisterToZero(r);
+
+               assertEquals("0000", am2901.getDirectly(r));
+               assertEquals("0000", am2901.getY());
+               assertEquals("0", am2901.getCarryOut());
+               assertEquals("0", am2901.getOverflow());
+               assertEquals("0", am2901.getSign());
+               assertEquals("1", am2901.getZero());
+       }
+
+       @Test
+       @Order(3)
+       @DisplayName("Setting all registers to 0")
+       void testSetAllToZero()
+       {
+               setRegistersToZero();
+
+               assertEquals("0000", am2901.getY());
+               assertEquals("0", am2901.getCarryOut());
+               assertEquals("0", am2901.getOverflow());
+               assertEquals("0", am2901.getSign());
+               assertEquals("1", am2901.getZero());
+               assertEquals("0", am2901.getQ_0());
+               assertEquals("0", am2901.getQ_3());
+               assertEquals("0", am2901.getRAM_0());
+               assertEquals("0", am2901.getRAM_3());
+
+               assertAll("register values", Register.stream().map(r -> () ->
+               {
+                       assertEquals("0000", am2901.getDirectly(r), r.name());
+               }));
+       }
+
+       @Test
+       @Order(4)
+       @DisplayName("ADD operation")
+       void testADD()
+       {
+               am2901.setSrc(DA);
+               am2901.setFunc(ADD);
+               am2901.setDest(NOP);
+               am2901.setReg_A(r0.toBitString());
+
+               assertAll(getAll4BitPairs().map(xy -> () ->
+               {
+                       am2901.setDirectly(r0, to4bitBin(xy.x));
+                       am2901.setD(to4bitBin(xy.y));
+
+                       am2901.assertFullCycleSuccess();
+
+                       int res32Bit = xy.x + xy.y;
+                       int res4Bit = res32Bit & 0b1111;
+                       int res32Bit_sgn = signed4ToSigned32(xy.x) + signed4ToSigned32(xy.y);
+                       int res4Bit_sgn = signed4ToSigned32(res32Bit_sgn);
+
+                       assertAll("Result of " + xy.x + " + " + xy.y + " = " + res32Bit,
+                                       () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), "    Y"),
+                                       () -> assertEquals(to1bitBin(res4Bit == 0), am2901.getZero(), "    F=0"),
+                                       () -> assertEquals(to1bitBin(res4Bit & 0b1000), am2901.getSign(), "    F3"),
+                                       () -> assertEquals(to1bitBin(res32Bit > 15), am2901.getCarryOut(), "    Cn+4"),
+                                       () -> assertEquals(to1bitBin(res4Bit_sgn != res32Bit_sgn), am2901.getOverflow(), "    OVR"));
+               }));
+       }
+
+       @Test
+       @Order(4)
+       @DisplayName("AND operation")
+       void testAND()
+       {
+               am2901.setSrc(DA);
+               am2901.setFunc(AND);
+               am2901.setDest(NOP);
+               am2901.setReg_A(r0.toBitString());
+
+               assertAll(getAll4BitPairs().map(xy -> () ->
+               {
+                       am2901.setDirectly(r0, to4bitBin(xy.x));
+                       am2901.setD(to4bitBin(xy.y));
+
+                       am2901.assertFullCycleSuccess();
+
+                       int res32Bit = xy.x & xy.y;
+
+                       assertAll("Result of " + xy.x + " & " + xy.y + " = " + res32Bit,
+                                       () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), "    Y"),
+                                       () -> assertEquals(to1bitBin(res32Bit == 0), am2901.getZero(), "    F=0"),
+                                       () -> assertEquals(to1bitBin(res32Bit & 0b1000), am2901.getSign(), "    F3")
+//                                     () -> assertEquals(to1bitBin(res32Bit), am2901.getCarryOut(), "    Cn+4"), // TODO
+//                                     () -> assertEquals(to1bitBin(res32Bit), am2901.getOverflow(), "    OVR") // TODO
+                       );
+               }));
+       }
+
+       @Test
+       @Order(4)
+       @DisplayName("OR operation")
+       void testOR()
+       {
+               am2901.setSrc(DA);
+               am2901.setFunc(OR);
+               am2901.setDest(NOP);
+               am2901.setReg_A(r0.toBitString());
+
+               assertAll(getAll4BitPairs().map(xy -> () ->
+               {
+                       am2901.setDirectly(r0, to4bitBin(xy.x));
+                       am2901.setD(to4bitBin(xy.y));
+
+                       am2901.assertFullCycleSuccess();
+
+                       int res32Bit = xy.x | xy.y;
+
+                       assertAll("Result of " + xy.x + " | " + xy.y + " = " + res32Bit,
+                                       () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), "    Y"),
+                                       () -> assertEquals(to1bitBin(res32Bit == 0), am2901.getZero(), "    F=0"),
+                                       () -> assertEquals(to1bitBin(res32Bit & 0b1000), am2901.getSign(), "    F3")
+//                                     () -> assertEquals(to1bitBin(res32Bit != 0b1111), am2901.getCarryOut(), "    Cn+4"), // TODO
+//                                     () -> assertEquals(to1bitBin(res32Bit != 0b1111), am2901.getOverflow(), "    OVR") // TODO
+                       );
+               }));
+       }
+
+       @Test
+       @Order(4)
+       @DisplayName("XOR operation")
+       void testXOR()
+       {
+               am2901.setSrc(DA);
+               am2901.setFunc(EXOR);
+               am2901.setDest(NOP);
+               am2901.setReg_A(r0.toBitString());
+
+               assertAll(getAll4BitPairs().map(xy -> () ->
+               {
+                       am2901.setDirectly(r0, to4bitBin(xy.x));
+                       am2901.setD(to4bitBin(xy.y));
+
+                       am2901.assertFullCycleSuccess();
+
+                       int res32Bit = xy.x ^ xy.y;
+
+                       assertAll("Result of " + xy.x + " ^ " + xy.y + " = " + res32Bit,
+                                       () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), "    Y"),
+                                       () -> assertEquals(to1bitBin(res32Bit == 0), am2901.getZero(), "    F=0"),
+                                       () -> assertEquals(to1bitBin(res32Bit & 0b1000), am2901.getSign(), "    F3"));
+               }));
+       }
+
+       @Test
+       @Order(4)
+       @DisplayName("SUB operation")
+       void testSUB()
+       {
+               am2901.setSrc(DA);
+               am2901.setCarryIn("1");
+               am2901.setFunc(SUBR);
+               am2901.setDest(NOP);
+               am2901.setReg_A(r0.toBitString());
+
+               assertAll(getAll4BitPairs().map(xy -> () ->
+               {
+                       am2901.setDirectly(r0, to4bitBin(xy.x));
+                       am2901.setD(to4bitBin(xy.y));
+
+                       am2901.assertFullCycleSuccess();
+
+                       int res32Bit = xy.x - xy.y;
+                       int res4Bit = res32Bit & 0b1111;
+                       int res32Bit_sgn = signed4ToSigned32(xy.x) - signed4ToSigned32(xy.y);
+                       int res4Bit_sgn = signed4ToSigned32(res32Bit_sgn);
+
+                       assertAll("Result of " + xy.x + " - " + xy.y + " = " + res32Bit,
+                                       () -> assertEquals(to4bitBin(res32Bit), am2901.getY(), "    Y"),
+                                       () -> assertEquals(to1bitBin(res4Bit == 0), am2901.getZero(), "    F=0"),
+                                       () -> assertEquals(to1bitBin(res4Bit & 0b1000), am2901.getSign(), "    F3"),
+                                       () -> assertEquals(to1bitBin(xy.x >= xy.y), am2901.getCarryOut(), "    Cn+4"),
+                                       () -> assertEquals(to1bitBin(res4Bit_sgn != res32Bit_sgn), am2901.getOverflow(), "    OVR"));
+               }));
+       }
+
+       static Stream<Point> getAll4BitPairs()
+       {
+               return IntStream.range(0, 16).boxed().flatMap(x -> IntStream.range(0, 16).mapToObj(y -> new Point(x, y)));
+       }
+}
diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/Am2901Testbench.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/Am2901Testbench.java
new file mode 100644 (file)
index 0000000..3a7ffa2
--- /dev/null
@@ -0,0 +1,102 @@
+package net.mograsim.logic.model.am2900.am2901;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+import net.mograsim.logic.model.SimpleLogicUIStandalone;
+import net.mograsim.logic.model.model.ViewModelModifiable;
+import net.mograsim.logic.model.model.components.GUIComponent;
+import net.mograsim.logic.model.model.components.atomic.GUIAndGate;
+import net.mograsim.logic.model.model.components.atomic.GUIBitDisplay;
+import net.mograsim.logic.model.model.components.atomic.GUIManualSwitch;
+import net.mograsim.logic.model.model.components.atomic.GUINotGate;
+import net.mograsim.logic.model.model.components.atomic.TextComponent;
+import net.mograsim.logic.model.model.wires.Pin;
+import net.mograsim.logic.model.model.wires.WireCrossPoint;
+import net.mograsim.logic.model.serializing.IndirectGUIComponentCreator;
+import net.mograsim.logic.model.util.ModellingTool;
+
+public class Am2901Testbench
+{
+       public static void main(String[] args)
+       {
+               SimpleLogicUIStandalone.executeVisualisation(Am2901Testbench::createTestbench);
+       }
+
+       public static void createTestbench(ViewModelModifiable model)
+       {
+               GUIComponent comp = IndirectGUIComponentCreator.createComponent(model, "GUIAm2901");
+               ModellingTool tool = ModellingTool.createFor(model);
+
+               comp.moveTo(240, 0);
+
+               GUIManualSwitch enable = new GUIManualSwitch(model, 1);
+               WireCrossPoint wcp0 = new WireCrossPoint(model, 1);
+               GUINotGate not1 = new GUINotGate(model, 1);
+               GUINotGate not2 = new GUINotGate(model, 1);
+               GUINotGate not3 = new GUINotGate(model, 1);
+               GUIAndGate and = new GUIAndGate(model, 1);
+               tool.connect(wcp0, enable, "");
+               tool.connect(wcp0, and, "A");
+               tool.connect(wcp0, not1, "A");
+               tool.connect(not1, not2, "Y", "A");
+               tool.connect(not2, not3, "Y", "A");
+               tool.connect(not3, and, "Y", "B");
+               enable.moveTo(20, -32.5);
+               wcp0.moveTo(35, -26);
+               not1.moveTo(50, -20);
+               not2.moveTo(80, -20);
+               not3.moveTo(110, -20);
+               and.moveTo(135, -30);
+               Pin last = and.getPin("Y");
+
+               // guess which pins are outputs and which are inputs
+               // TODO this code exists four times... but it seems too "hacky" to put it in a helper class
+               List<String> inputPinNames = new ArrayList<>();
+               List<String> outputPinNames = new ArrayList<>();
+               for (Pin p : comp.getPins().values())
+                       if (p.getRelX() == 0)
+                               inputPinNames.add(p.name);
+                       else
+                               outputPinNames.add(p.name);
+
+               inputPinNames.sort(Comparator.comparing(comp::getPin, Comparator.comparing(Pin::getRelY)));
+               outputPinNames.sort(Comparator.comparing(comp::getPin, Comparator.comparing(Pin::getRelY)));
+
+               for (int i = 0; i < inputPinNames.size(); i++)
+               {
+                       double x = 55 + 70 * (i % 2);
+                       double y = 10 * i;
+
+                       WireCrossPoint wcp = new WireCrossPoint(model, 1);
+                       GUIComponent d_ff = IndirectGUIComponentCreator.createComponent(model, "GUIdff");
+                       GUIManualSwitch sw = new GUIManualSwitch(model, 1);
+
+                       tool.connect(last, wcp);
+                       tool.connect(wcp, d_ff, "C");
+                       tool.connect(sw, d_ff, "", "D");
+                       tool.connect(d_ff, comp, "Q", inputPinNames.get(i));
+                       last = wcp.getPin();
+
+                       TextComponent label = new TextComponent(model, inputPinNames.get(i));
+
+                       sw.moveTo(x, y + 7.5);
+                       wcp.moveTo(160, y);
+                       d_ff.moveTo(170, y);
+                       label.moveTo(x - 48, y + 8);
+               }
+
+               for (int i = 0; i < outputPinNames.size(); i++)
+               {
+                       double x = 300 + 75 * (i % 2);
+                       double y = 10 * i - 2.5;
+                       GUIBitDisplay bd = new GUIBitDisplay(model, 1);
+                       bd.moveTo(x, y);
+                       tool.connect(bd.getInputPin(), comp, outputPinNames.get(i));
+
+                       TextComponent label = new TextComponent(model, outputPinNames.get(i));
+                       label.moveTo(x + 25, y);
+               }
+       }
+}
\ No newline at end of file
diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901.java
new file mode 100644 (file)
index 0000000..34c179d
--- /dev/null
@@ -0,0 +1,108 @@
+package net.mograsim.logic.model.am2900.am2901;
+
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+import net.mograsim.logic.model.am2900.TestUtil;
+import net.mograsim.logic.model.am2900.TestableCircuit;
+
+public interface TestableAm2901 extends TestableCircuit
+{
+
+       void setDest(Am2901_Dest dest);
+
+       void setFunc(Am2901_Func func);
+
+       void setSrc(Am2901_Src src);
+
+       void setReg_A(String val_4_bit);
+
+       void setReg_B(String val_4_bit);
+
+       void setCarryIn(String val_1_bit);
+
+       void setNotOutEnable(String val_1_bit);
+
+       void setD(String val_4_bit);
+
+       void setQ_0(String val_1_bit);
+
+       void setQ_3(String val_1_bit);
+
+       void setRAM_0(String val_1_bit);
+
+       void setRAM_3(String val_1_bit);
+
+       void setDirectly(Register r, String val_4_bit);
+
+       String getQ_0();
+
+       String getQ_3();
+
+       String getRAM_0();
+
+       String getRAM_3();
+
+       String getNotP();
+
+       String getNotG();
+
+       String getCarryOut();
+
+       String getSign();
+
+       String getZero();
+
+       String getOverflow();
+
+       String getY();
+
+       String getDirectly(Register r);
+
+       public enum Am2901_Dest
+       {
+               QREG, NOP, RAMA, RAMF, RAMQD, RAMD, RAMQU, RAMU;
+
+               public boolean doesShift()
+               {
+                       return ordinal() >= 4;
+               }
+
+               public int getShiftDir()
+               {
+                       return doesShift() ? (ordinal() < 6 ? -1 : 1) : 0;
+               }
+
+               public int getI7()
+               {
+                       return this.ordinal() >> 1 & 1;
+               }
+       }
+
+       public enum Am2901_Func
+       {
+               ADD, SUBR, SUBS, OR, AND, NOTRS, EXOR, EXNOR;
+       }
+
+       public enum Am2901_Src
+       {
+               AQ, AB, ZQ, ZB, ZA, DA, DQ, DZ;
+       }
+
+       public enum Register
+       {
+               r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, rA, rB, rC, rD, rE, rF, Q;
+
+               public String toBitString()
+               {
+                       if (this.ordinal() > 0xF)
+                               throw new UnsupportedOperationException();
+                       return TestUtil.to4bitBin(this.ordinal());
+               }
+
+               public static Stream<Register> stream()
+               {
+                       return Arrays.stream(values());
+               }
+       }
+}
diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901Impl.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901Impl.java
new file mode 100644 (file)
index 0000000..34c68c1
--- /dev/null
@@ -0,0 +1,229 @@
+package net.mograsim.logic.model.am2900.am2901;
+
+import net.mograsim.logic.core.components.BitDisplay;
+import net.mograsim.logic.core.components.ManualSwitch;
+import net.mograsim.logic.core.timeline.Timeline;
+import net.mograsim.logic.core.types.Bit;
+import net.mograsim.logic.core.types.BitVector;
+import net.mograsim.logic.model.am2900.TestEnvironmentHelper;
+import net.mograsim.logic.model.am2900.TestEnvironmentHelper.DebugState;
+import net.mograsim.logic.model.am2900.TestUtil;
+import net.mograsim.logic.model.model.components.GUIComponent;
+
+public class TestableAm2901Impl implements TestableAm2901
+{
+       private GUIComponent am2901;
+       private Timeline timeline;
+       private ManualSwitch I8, I7, I6, I5, I4, I3, I2, I1, I0;
+       private ManualSwitch C;
+       private ManualSwitch Cn;
+       private ManualSwitch D1, D2, D3, D4;
+       private ManualSwitch A0, A1, A2, A3;
+       private ManualSwitch B0, B1, B2, B3;
+       private ManualSwitch IRAMn, IRAMn_3, IQn, IQn_3;
+       private BitDisplay Y1, Y2, Y3, Y4;
+       private BitDisplay F_0, Cn_4, OVR, F3;
+       private BitDisplay ORAMn, ORAMn_3, OQn, OQn_3;
+
+       private final TestEnvironmentHelper testHelper = new TestEnvironmentHelper(this, "GUIAm2901");
+
+       @Override
+       public Result run()
+       {
+               return testHelper.run();
+       }
+
+       @Override
+       public void setup()
+       {
+               testHelper.setup(DebugState.NO_DEBUG);
+       }
+
+       @Override
+       public void setDest(Am2901_Dest dest)
+       {
+               var bits = TestUtil.of(dest.ordinal(), 3);
+               I8.setState(bits.getLSBit(2));
+               I7.setState(bits.getLSBit(1));
+               I6.setState(bits.getLSBit(0));
+       }
+
+       @Override
+       public void setFunc(Am2901_Func func)
+       {
+               var bits = TestUtil.of(func.ordinal(), 3);
+               I5.setState(bits.getLSBit(2));
+               I4.setState(bits.getLSBit(1));
+               I3.setState(bits.getLSBit(0));
+       }
+
+       @Override
+       public void setSrc(Am2901_Src src)
+       {
+               var bits = TestUtil.of(src.ordinal(), 3);
+               I2.setState(bits.getLSBit(2));
+               I1.setState(bits.getLSBit(1));
+               I0.setState(bits.getLSBit(0));
+       }
+
+       @Override
+       public void setReg_A(String val_4_bit)
+       {
+               var bits = BitVector.parse(val_4_bit);
+               A3.setState(bits.getLSBit(3));
+               A2.setState(bits.getLSBit(2));
+               A1.setState(bits.getLSBit(1));
+               A0.setState(bits.getLSBit(0));
+       }
+
+       @Override
+       public void setReg_B(String val_4_bit)
+       {
+               var bits = BitVector.parse(val_4_bit);
+               B3.setState(bits.getLSBit(3));
+               B2.setState(bits.getLSBit(2));
+               B1.setState(bits.getLSBit(1));
+               B0.setState(bits.getLSBit(0));
+       }
+
+       @Override
+       public void setCarryIn(String val_1_bit)
+       {
+               Cn.setState(Bit.parse(val_1_bit));
+       }
+
+       @Override
+       public void setNotOutEnable(String val_1_bit)
+       {
+               throw new UnsupportedOperationException(); // TODO
+       }
+
+       @Override
+       public void setD(String val_4_bit)
+       {
+               var bits = BitVector.parse(val_4_bit);
+               D4.setState(bits.getLSBit(3));
+               D3.setState(bits.getLSBit(2));
+               D2.setState(bits.getLSBit(1));
+               D1.setState(bits.getLSBit(0));
+       }
+
+       @Override
+       public void setQ_0(String val_1_bit)
+       {
+               IQn.setState(Bit.parse(val_1_bit));
+       }
+
+       @Override
+       public void setQ_3(String val_1_bit)
+       {
+               IQn_3.setState(Bit.parse(val_1_bit));
+       }
+
+       @Override
+       public void setRAM_0(String val_1_bit)
+       {
+               IRAMn.setState(Bit.parse(val_1_bit));
+       }
+
+       @Override
+       public void setRAM_3(String val_1_bit)
+       {
+               IRAMn_3.setState(Bit.parse(val_1_bit));
+       }
+
+       @Override
+       public void clockOn(boolean isClockOn)
+       {
+               C.setState(isClockOn ? Bit.ONE : Bit.ZERO);
+       }
+
+       @Override
+       public String getQ_0()
+       {
+               return OQn.getDisplayedValue().toString();
+       }
+
+       @Override
+       public String getQ_3()
+       {
+               return OQn_3.getDisplayedValue().toString();
+       }
+
+       @Override
+       public String getRAM_0()
+       {
+               return ORAMn.getDisplayedValue().toString();
+       }
+
+       @Override
+       public String getRAM_3()
+       {
+               return ORAMn_3.getDisplayedValue().toString();
+       }
+
+       @Override
+       public String getNotP()
+       {
+               throw new UnsupportedOperationException(); // TODO
+       }
+
+       @Override
+       public String getNotG()
+       {
+               throw new UnsupportedOperationException(); // TODO
+       }
+
+       @Override
+       public String getCarryOut()
+       {
+               return Cn_4.getDisplayedValue().toString();
+       }
+
+       @Override
+       public String getSign()
+       {
+               return F3.getDisplayedValue().toString();
+       }
+
+       @Override
+       public String getZero()
+       {
+               return F_0.getDisplayedValue().toString();
+       }
+
+       @Override
+       public String getOverflow()
+       {
+               return OVR.getDisplayedValue().toString();
+       }
+
+       @Override
+       public String getY()
+       {
+               var y3 = Y4.getDisplayedValue();
+               var y2 = Y3.getDisplayedValue();
+               var y1 = Y2.getDisplayedValue();
+               var y0 = Y1.getDisplayedValue();
+               return y3.concat(y2).concat(y1).concat(y0).toString();
+       }
+
+       @Override
+       public void setDirectly(Register r, String val_4_bit)
+       {
+               am2901.setHighLevelState(regToStateID(r), BitVector.parse(val_4_bit));
+       }
+
+       @Override
+       public String getDirectly(Register r)
+       {
+               return ((BitVector) am2901.getHighLevelState(regToStateID(r))).toString();
+       }
+
+       private static String regToStateID(Register r)
+       {
+               if (r == Register.Q)
+                       return "qreg.q";
+               return "regs.c" + r.toBitString() + ".q";
+       }
+}