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