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