Restructured the Preferences system
[Mograsim.git] / tests / net.mograsim.logic.model.am2900.tests / src / net / mograsim / logic / model / am2900 / TestGCD.java
1 package net.mograsim.logic.model.am2900;
2
3 import static org.junit.jupiter.api.Assertions.assertEquals;
4
5 import java.io.IOException;
6 import java.util.concurrent.ThreadLocalRandom;
7 import java.util.concurrent.atomic.AtomicInteger;
8 import java.util.concurrent.atomic.AtomicReference;
9 import java.util.stream.IntStream;
10
11 import org.junit.jupiter.api.AfterAll;
12 import org.junit.jupiter.api.BeforeAll;
13 import org.junit.jupiter.api.Test;
14 import org.junit.jupiter.api.TestInstance;
15 import org.junit.jupiter.api.TestInstance.Lifecycle;
16 import org.junit.jupiter.params.ParameterizedTest;
17 import org.junit.jupiter.params.provider.MethodSource;
18
19 import net.mograsim.logic.core.types.BitVector;
20 import net.mograsim.logic.model.LogicUIStandaloneGUI;
21 import net.mograsim.logic.model.am2900.machine.Am2900Machine;
22 import net.mograsim.logic.model.am2900.machine.Am2900MainMemoryDefinition;
23 import net.mograsim.logic.model.am2900.machine.Am2900MicroInstructionDefinition;
24 import net.mograsim.logic.model.am2900.machine.Am2900MicroInstructionMemoryDefinition;
25 import net.mograsim.logic.model.am2900.machine.StrictAm2900MachineDefinition;
26 import net.mograsim.logic.model.am2900.machine.registers.am2901.NumberedRegister;
27 import net.mograsim.logic.model.preferences.DefaultRenderPreferences;
28 import net.mograsim.machine.MainMemory;
29 import net.mograsim.machine.mi.MicroInstruction;
30 import net.mograsim.machine.mi.MicroInstructionDefinition;
31 import net.mograsim.machine.mi.MicroInstructionMemory;
32 import net.mograsim.machine.mi.MicroInstructionMemoryParser;
33 import net.mograsim.machine.mi.StandardMicroInstructionMemory;
34 import net.mograsim.machine.mi.parameters.MicroInstructionParameter;
35 import net.mograsim.machine.mi.parameters.ParameterClassification;
36 import net.mograsim.machine.standard.memory.WordAddressableMemory;
37
38 @TestInstance(Lifecycle.PER_CLASS)
39 public class TestGCD
40 {
41         private static final boolean startGUI = false;
42
43         private Am2900Machine mach;
44         private MicroInstructionMemory mpm;
45         private MainMemory ram;
46         private AtomicInteger machState;
47         private AtomicInteger halfCycleCount;
48         private AtomicReference<MicroInstructionMemory> mpmToSetNextCycle;
49
50         @BeforeAll
51         public void setupMachine() throws IOException
52         {
53                 Am2900Loader.setup();
54                 mach = new StrictAm2900MachineDefinition().createNew();
55                 mpm = MicroInstructionMemoryParser.parseMemory(Am2900MicroInstructionMemoryDefinition.instance,
56                                 TestGCD.class.getResourceAsStream("gcd.mpm"));
57                 ram = new WordAddressableMemory(Am2900MainMemoryDefinition.instance);
58                 mach.getMainMemory().bind(ram);
59
60                 if (startGUI)
61                         startGUI();
62
63                 machState = new AtomicInteger(2);
64                 halfCycleCount = new AtomicInteger();
65                 // needed for avoiding hazard loops in the first muIR
66                 mpmToSetNextCycle = new AtomicReference<>();
67                 mach.getClock().registerObserver(c ->
68                 {
69                         halfCycleCount.addAndGet(1);
70                         synchronized (machState)
71                         {
72                                 long muPC = mach.getActiveMicroInstructionAddress();
73                                 if (muPC == 0)
74                                 {
75                                         machState.set(0);
76                                         machState.notify();
77                                 } else if (muPC == 0x12)
78                                 {
79                                         machState.set(1);
80                                         machState.notify();
81                                 }
82                                 if (!mach.getClock().isOn())
83                                 {
84                                         MicroInstructionMemory mpmToSet = mpmToSetNextCycle.getAndSet(null);
85                                         if (mpmToSet != null)
86                                                 mach.getMicroInstructionMemory().bind(mpmToSet);
87                                 }
88                         }
89                 });
90
91                 mach.reset();
92
93                 Thread execT = new Thread(mach.getTimeline()::executeAll, "Logic executer");
94                 execT.setDaemon(true);
95                 execT.start();
96
97                 System.out.println("Machine initialized");
98
99         }
100
101         private void startGUI()
102         {
103                 new Thread(() -> new LogicUIStandaloneGUI(mach.getModel(), new DefaultRenderPreferences()).run(), "GUI thread").start();
104         }
105
106         @Test
107         public void testGCDHardcodedCases() throws InterruptedException
108         {
109                 checkGCD(4, 12);
110                 checkGCD(4, 13);
111                 checkGCD(0, 3);
112                 checkGCD(4, 0);
113                 checkGCD(0, 0);
114                 checkGCD(48820, 8480);
115                 checkGCD(21420, 11288);
116                 checkGCD(15862, 21219);
117         }
118
119         @ParameterizedTest
120         @MethodSource("generateRandomInts")
121         public void testGCDRandomCases(int i) throws InterruptedException
122         {
123                 int a = i & 0xFFFF;
124                 int b = (i >>> 16);
125                 checkGCD(a, b);
126         }
127
128         public static IntStream generateRandomInts()
129         {
130                 return ThreadLocalRandom.current().ints(10);
131         }
132
133         private void checkGCD(int euclidA, int euclidB) throws InterruptedException
134         {
135                 int exp = gcd(euclidA, euclidB);
136                 System.out.println("Checking gcd(" + euclidA + ", " + euclidB + ") + (expected " + exp + ")");
137                 int act = executeGCD(euclidA, euclidB);
138                 assertEquals(exp, act);
139         }
140
141         private static int gcd(int a, int b)
142         {
143                 if (b == 0)
144                         return a;
145                 return gcd(b, a % b);
146         }
147
148         private int executeGCD(int euclidA, int euclidB) throws InterruptedException
149         {
150                 ram.setCell(0, BitVector.from(euclidA, 16));
151                 ram.setCell(1, BitVector.from(euclidB, 16));
152
153                 resetMachine();
154
155                 synchronized (machState)
156                 {
157                         while (machState.get() != 1)
158                                 machState.wait();
159                 }
160
161                 BitVector result = mach.getRegister(NumberedRegister.instancesCorrectOrder.get(1));
162                 return result.isBinary() ? (int) result.getUnsignedValueLong() : -1;
163         }
164
165         private void resetMachine() throws InterruptedException
166         {
167                 MicroInstructionDefinition muiDef = Am2900MicroInstructionDefinition.instance;
168                 ParameterClassification[] paramClassifications = muiDef.getParameterClassifications();
169                 MicroInstructionParameter[] defaultParams = muiDef.createDefaultInstruction().getParameters();
170                 defaultParams[19] = paramClassifications[19].parse("JZ");
171                 MicroInstruction jzMI = MicroInstruction.create(defaultParams);
172
173                 MicroInstructionMemory jzMPM = new StandardMicroInstructionMemory(Am2900MicroInstructionMemoryDefinition.instance);
174                 jzMPM.setCell(0x00, jzMI);
175                 jzMPM.setCell(0x13, jzMI);
176                 mpmToSetNextCycle.set(jzMPM);
177
178                 synchronized (machState)
179                 {
180                         while (machState.get() != 0)
181                                 machState.wait();
182                 }
183
184                 mpmToSetNextCycle.set(mpm);
185         }
186
187         @AfterAll
188         public void printStats()
189         {
190                 System.out.println("Machine executed " + halfCycleCount.get() + " cycles in total (including JZs)");
191         }
192 }