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