Did some clean up
[Mograsim.git] / era.mi / src / era / mi / logic / wires / WireArray.java
1 package era.mi.logic.wires;\r
2 \r
3 import java.io.Closeable;\r
4 import java.util.ArrayList;\r
5 import java.util.Arrays;\r
6 import java.util.Iterator;\r
7 import java.util.List;\r
8 \r
9 import era.mi.logic.Bit;\r
10 import era.mi.logic.Simulation;\r
11 import era.mi.logic.Util;\r
12 \r
13 /**\r
14  * Represents an array of wires that can store n bits of information.\r
15  * \r
16  * @author Fabian Stemmler\r
17  *\r
18  */\r
19 public class WireArray\r
20 {\r
21         private Bit[] values;\r
22         public final int travelTime;\r
23         private List<WireArrayObserver> observers = new ArrayList<WireArrayObserver>();\r
24         public final int length;\r
25         List<WireArrayEnd> inputs = new ArrayList<WireArrayEnd>();\r
26 \r
27         public WireArray(int length, int travelTime)\r
28         {\r
29                 if (length < 1)\r
30                         throw new IllegalArgumentException(\r
31                                         String.format("Tried to create an array of wires with length %d, but a length of less than 1 makes no sense.", length));\r
32                 this.length = length;\r
33                 this.travelTime = travelTime;\r
34                 initValues();\r
35         }\r
36 \r
37         private void initValues()\r
38         {\r
39                 values = Bit.U.makeArray(length);\r
40         }\r
41 \r
42         private void recalculateSingleInput()\r
43         {\r
44                 WireArrayEnd input = inputs.get(0);\r
45                 if (!Arrays.equals(input.getValues(), values))\r
46                 {\r
47                         Bit[] oldValues = values.clone();\r
48                         System.arraycopy(input.getValues(), 0, values, 0, length);\r
49                         notifyObservers(oldValues);\r
50                 }\r
51         }\r
52 \r
53         private void recalculateMultipleInputs()\r
54         {\r
55                 Iterator<WireArrayEnd> it = inputs.iterator();\r
56                 Bit[] newValues = it.next().inputValues.clone();\r
57 \r
58                 while (it.hasNext())\r
59                 {\r
60                         WireArrayEnd input = it.next();\r
61                         Bit[] bits = input.getValues();\r
62                         for (int i = 0; i < length; i++)\r
63                         {\r
64                                 newValues[i] = newValues[i].combineWith(bits[i]);\r
65                         }\r
66                 }\r
67 \r
68                 if (!Arrays.equals(newValues, values))\r
69                 {\r
70                         Bit[] oldValues = values;\r
71                         values = newValues;\r
72                         notifyObservers(oldValues);\r
73                 }\r
74         }\r
75 \r
76         void recalculate()\r
77         {\r
78                 switch (inputs.size())\r
79                 {\r
80                 case 0:\r
81                         return;\r
82                 case 1:\r
83                         recalculateSingleInput();\r
84                         break;\r
85                 default:\r
86                         recalculateMultipleInputs();\r
87                 }\r
88         }\r
89 \r
90         /**\r
91          * The WireArray is interpreted as an unsigned integer with n bits.\r
92          * \r
93          * @return <code>true</code> if all bits are either <code>Bit.ONE</code> or <code>Bit.ZERO</code> (they do not all have to have the same\r
94          *         value), not <code>Bit.X</code> or <code>Bit.Z</code>. <code>false</code> is returned otherwise.\r
95          * \r
96          * @author Fabian Stemmler\r
97          */\r
98         public boolean hasNumericValue()\r
99         {\r
100                 for (Bit b : values)\r
101                 {\r
102                         if (b != Bit.ZERO && b != Bit.ONE)\r
103                                 return false;\r
104                 }\r
105                 return true;\r
106         }\r
107 \r
108         /**\r
109          * The WireArray is interpreted as an unsigned integer with n bits.\r
110          * \r
111          * @return The unsigned value of the {@link WireArray}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.\r
112          * \r
113          * @author Fabian Stemmler\r
114          */\r
115         public long getUnsignedValue()\r
116         {\r
117                 long val = 0;\r
118                 long mask = 1;\r
119                 for (int i = 0; i < length; i++)\r
120                 {\r
121                         switch (values[i])\r
122                         {\r
123                         default:\r
124                         case Z:\r
125                         case X:\r
126                                 return 0; // TODO: Proper handling for getUnsignedValue(), if not all bits are 1 or 0;\r
127                         // Random number?\r
128                         case ONE:\r
129                                 val |= mask;\r
130                                 break;\r
131                         case ZERO:\r
132                         }\r
133                         mask = mask << 1;\r
134                 }\r
135                 return val;\r
136         }\r
137 \r
138         /**\r
139          * The WireArray is interpreted as a signed integer with n bits.\r
140          * \r
141          * @return The signed value of the {@link WireArray}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.\r
142          * \r
143          * @author Fabian Stemmler\r
144          */\r
145         public long getSignedValue()\r
146         {\r
147                 long val = getUnsignedValue();\r
148                 long mask = 1 << (length - 1);\r
149                 if ((mask & val) != 0)\r
150                 {\r
151                         int shifts = 64 - length;\r
152                         return (val << shifts) >> shifts;\r
153                 }\r
154                 return val;\r
155         }\r
156 \r
157         /**\r
158          * Included for convenient use on {@link WireArray}s of length 1.\r
159          * \r
160          * @return The value of bit 0.\r
161          * \r
162          * @author Fabian Stemmler\r
163          */\r
164         public Bit getValue()\r
165         {\r
166                 return getValue(0);\r
167         }\r
168 \r
169         /**\r
170          * \r
171          * @param index Index of the requested bit.\r
172          * @return The value of the indexed bit.\r
173          * \r
174          * @author Fabian Stemmler\r
175          */\r
176         public Bit getValue(int index)\r
177         {\r
178                 return values[index];\r
179         }\r
180 \r
181         public Bit[] getValues(int start, int end)\r
182         {\r
183                 int length = end - start;\r
184                 Bit[] bits = new Bit[length];\r
185                 System.arraycopy(values, start, bits, 0, length);\r
186                 return bits;\r
187         }\r
188 \r
189         /**\r
190          * @return An array of length n containing the values of the n bits in the {@link WireArray}. Can be safely modified.\r
191          * \r
192          * @author Fabian Stemmler\r
193          */\r
194         public Bit[] getValues()\r
195         {\r
196                 return values.clone();\r
197         }\r
198 \r
199         /**\r
200          * Adds an {@link WireArrayObserver}, who will be notified when the value of the {@link WireArray} is updated.\r
201          * \r
202          * @param ob The {@link WireArrayObserver} to be notified of changes.\r
203          * @return true if the given {@link WireArrayObserver} was not already registered, false otherwise\r
204          * \r
205          * @author Fabian Stemmler\r
206          */\r
207         public boolean addObserver(WireArrayObserver ob)\r
208         {\r
209                 return observers.add(ob);\r
210         }\r
211 \r
212         private void notifyObservers(Bit[] oldValues)\r
213         {\r
214                 for (WireArrayObserver o : observers)\r
215                         o.update(this, oldValues);\r
216         }\r
217 \r
218         /**\r
219          * Create and register a {@link WireArrayEnd} object, which is tied to this {@link WireArray}.\r
220          */\r
221         public WireArrayEnd createInput()\r
222         {\r
223                 return new WireArrayEnd(this);\r
224         }\r
225 \r
226         void registerInput(WireArrayEnd toRegister)\r
227         {\r
228                 inputs.add(toRegister);\r
229         }\r
230 \r
231         /**\r
232          * A {@link WireArrayEnd} feeds a constant signal into the {@link WireArray} it is tied to. The combination of all inputs determines the\r
233          * {@link WireArray}s final value. X dominates all other inputs Z does not affect the final value, unless there are no other inputs than\r
234          * Z 0 and 1 turn into X when they are mixed\r
235          * \r
236          * @author Fabian Stemmler\r
237          */\r
238         public class WireArrayEnd implements Closeable\r
239         {\r
240                 public final WireArray owner;\r
241                 private boolean open;\r
242                 Bit[] inputValues;\r
243 \r
244                 WireArrayEnd(WireArray owner)\r
245                 {\r
246                         super();\r
247                         this.owner = owner;\r
248                         open = true;\r
249                         initValues();\r
250                         owner.registerInput(this);\r
251                 }\r
252 \r
253                 private void initValues()\r
254                 {\r
255                         inputValues = Bit.U.makeArray(length);\r
256                 }\r
257 \r
258                 /**\r
259                  * Sets the wires values. This takes up time, as specified by the {@link WireArray}s travel time.\r
260                  * \r
261                  * @param newValues The new values the wires should take on.\r
262                  * \r
263                  * @author Fabian Stemmler\r
264                  */\r
265                 public void feedSignals(Bit... newValues)\r
266                 {\r
267                         if (newValues.length != length)\r
268                                 throw new IllegalArgumentException(\r
269                                                 String.format("Attempted to input %d bits instead of %d bits.", newValues.length, length));\r
270                         feedSignals(0, newValues);\r
271 \r
272                 }\r
273 \r
274                 /**\r
275                  * Sets values of a subarray of wires. This takes up time, as specified by the {@link WireArray}s travel time.\r
276                  * \r
277                  * @param newValues   The new values the wires should take on.\r
278                  * @param startingBit The first index of the subarray of wires.\r
279                  * \r
280                  * @author Fabian Stemmler\r
281                  */\r
282                 public void feedSignals(int startingBit, Bit... newValues)\r
283                 {\r
284                         if (!open)\r
285                                 throw new RuntimeException("Attempted to write to closed WireArrayEnd.");\r
286                         Simulation.TIMELINE.addEvent((e) -> setValues(startingBit, newValues), travelTime);\r
287                 }\r
288 \r
289                 private void setValues(int startingBit, Bit... newValues)\r
290                 {\r
291                         int exclLastIndex = startingBit + newValues.length;\r
292                         if (length < exclLastIndex)\r
293                                 throw new ArrayIndexOutOfBoundsException(\r
294                                                 String.format("Attempted to input bits from index %d to %d when there are only %d wires.", startingBit,\r
295                                                                 exclLastIndex - 1, length));\r
296                         if (!Arrays.equals(inputValues, startingBit, exclLastIndex, newValues, 0, newValues.length))\r
297                         {\r
298                                 System.arraycopy(newValues, 0, inputValues, startingBit, newValues.length);\r
299                                 owner.recalculate();\r
300                         }\r
301                 }\r
302 \r
303                 /**\r
304                  * Returns a copy (safe to modify) of the values the {@link WireArrayEnd} is currently feeding into the associated\r
305                  * {@link WireArray}.\r
306                  */\r
307                 public Bit[] getValues()\r
308                 {\r
309                         return inputValues.clone();\r
310                 }\r
311 \r
312                 /**\r
313                  * {@link WireArrayEnd} now feeds Z into the associated {@link WireArray}.\r
314                  */\r
315                 public void clearSignals()\r
316                 {\r
317                         feedSignals(Bit.Z.makeArray(length));\r
318                 }\r
319 \r
320                 public Bit[] wireValuesExcludingMe()\r
321                 {\r
322                         Bit[] bits = Bit.Z.makeArray(length);\r
323                         for (WireArrayEnd wai : inputs)\r
324                         {\r
325                                 if (wai == this)\r
326                                         continue;\r
327                                 Util.combineInto(bits, wai.getValues());\r
328                         }\r
329                         return bits;\r
330                 }\r
331 \r
332                 public Bit getWireValue()\r
333                 {\r
334                         return owner.getValue();\r
335                 }\r
336 \r
337                 public Bit[] getWireValues()\r
338                 {\r
339                         return owner.getValues();\r
340                 }\r
341 \r
342                 @Override\r
343                 public String toString()\r
344                 {\r
345                         return Arrays.toString(inputValues);\r
346                 }\r
347 \r
348                 @Override\r
349                 public void close()\r
350                 {\r
351                         inputs.remove(this);\r
352                         open = false;\r
353                 }\r
354         }\r
355 \r
356         @Override\r
357         public String toString()\r
358         {\r
359                 return String.format("wire 0x%08x value: %s inputs: %s", hashCode(), Arrays.toString(values), inputs);\r
360         }\r
361 \r
362         public static WireArrayEnd[] extractInputs(WireArray[] w)\r
363         {\r
364                 WireArrayEnd[] inputs = new WireArrayEnd[w.length];\r
365                 for (int i = 0; i < w.length; i++)\r
366                         inputs[i] = w[i].createInput();\r
367                 return inputs;\r
368         }\r
369 }