add55f719326acf153694564c41b57b9b840053c
[Mograsim.git] / era.mi / src / era / mi / logic / wires / WireArray.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 WireArray
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<WireArrayInput> inputs = new ArrayList<WireArrayInput>();
25
26         public WireArray(int length, int travelTime)
27         {
28                 if (length < 1)
29                         throw new IllegalArgumentException(String.format("Tried to create an array of wires with length %o, but a length of less than 1 makes no sense.", length));
30                 this.length = length;
31                 this.travelTime = travelTime;
32                 initValues();
33         }
34
35         private void initValues()
36         {
37                 values = Bit.Z.makeArray(length);
38         }
39
40         private void recalculateSingleInput()
41         {
42                 WireArrayInput input = inputs.get(0);
43                 if (!Arrays.equals(input.getValues(), values))
44                 {
45                         System.arraycopy(input.getValues(), 0, values, 0, length);
46                         notifyObservers();
47                 }
48         }
49
50         private void recalculateMultipleInputs()
51         {
52                 Iterator<WireArrayInput> it = inputs.iterator();
53                 Bit[] newValues = it.next().inputValues.clone();
54
55                 while (it.hasNext())
56                 {
57                         WireArrayInput input = it.next();
58                         Bit[] bits = input.getValues();
59                         for (int i = 0; i < length; i++)
60                         {
61                                 if (Bit.Z.equals(bits[i]) || newValues[i].equals(bits[i]))
62                                         continue;
63                                 else if (Bit.Z.equals(newValues[i]))
64                                         newValues[i] = bits[i];
65                                 else
66                                         newValues[i] = Bit.X;
67                         }
68                 }
69
70                 if (!Arrays.equals(newValues, values))
71                 {
72                         notifyObservers();
73                         values = newValues;
74                 }
75         }
76
77         private void recalculate()
78         {
79                 switch (inputs.size())
80                 {
81                 case 0:
82                         return;
83                 case 1:
84                         recalculateSingleInput();
85                         break;
86                 default:
87                         recalculateMultipleInputs();
88                 }
89         }
90
91         /**
92          * The WireArray is interpreted as an unsigned integer with n bits.
93          * 
94          * @return <code>true</code> if all bits are either <code>Bit.ONE</code> or
95          *         <code>Bit.ZERO</code> (they do not all have to have the same value),
96          *         not <code>Bit.X</code> or <code>Bit.Z</code>. <code>false</code> is
97          *         returned otherwise.
98          * 
99          * @author Fabian Stemmler
100          */
101         public boolean hasNumericValue()
102         {
103                 for (Bit b : values)
104                 {
105                         if (b != Bit.ZERO && b != Bit.ONE)
106                                 return false;
107                 }
108                 return true;
109         }
110
111         /**
112          * The WireArray is interpreted as an unsigned integer with n bits.
113          * 
114          * @return The unsigned value of the {@link WireArray}'s bits, where value 0
115          *         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 WireArray is interpreted as a signed integer with n bits.
144          * 
145          * @return The signed value of the {@link WireArray}'s bits, where value 0
146          *         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
196          *         {@link WireArray}. Can be safely modified.
197          * 
198          * @author Fabian Stemmler
199          */
200         public Bit[] getValues()
201         {
202                 return values.clone();
203         }
204
205         /**
206          * Adds an {@link WireArrayObserver}, who will be notified when the value of the
207          * {@link WireArray} is updated.
208          * 
209          * @param ob The {@link WireArrayObserver} to be notified of changes.
210          * @return true if the given {@link WireArrayObserver} was not already
211          *         registered, false otherwise
212          * 
213          * @author Fabian Stemmler
214          */
215         public boolean addObserver(WireArrayObserver ob)
216         {
217                 return observers.add(ob);
218         }
219
220         private void notifyObservers()
221         {
222                 for (WireArrayObserver o : observers)
223                         o.update(this);
224         }
225
226         /**
227          * Create and register a {@link WireArrayInput} object, which is tied to this
228          * {@link WireArray}.
229          */
230         public WireArrayInput createInput()
231         {
232                 return new WireArrayInput(this);
233         }
234
235         private void registerInput(WireArrayInput toRegister)
236         {
237                 inputs.add(toRegister);
238         }
239
240         /**
241          * A {@link WireArrayInput} feeds a constant signal into the {@link WireArray}
242          * it is tied to. The combination of all inputs determines the
243          * {@link WireArray}s final value. X dominates all other inputs Z does not
244          * affect the final value, unless there are no other inputs than Z 0 and 1 turn
245          * into X when they are mixed
246          * 
247          * @author Fabian Stemmler
248          */
249         public class WireArrayInput
250         {
251                 public final WireArray owner;
252                 private Bit[] inputValues;
253
254                 private WireArrayInput(WireArray owner)
255                 {
256                         super();
257                         this.owner = owner;
258                         initValues();
259                         owner.registerInput(this);
260                 }
261
262                 private void initValues()
263                 {
264                         inputValues = Bit.Z.makeArray(length);
265                 }
266
267                 /**
268                  * Sets the wires values. This takes up time, as specified by the
269                  * {@link WireArray}s travel time.
270                  * 
271                  * @param newValues The new values the wires should take on.
272                  * 
273                  * @author Fabian Stemmler
274                  */
275                 public void feedSignals(Bit... newValues)
276                 {
277                         if (newValues.length == length)
278                         {
279                                 feedSignals(0, newValues);
280                         } else
281                                 throw new IllegalArgumentException(String.format("Attempted to input %o bits instead of %o bits.", newValues.length, length));
282                 }
283
284                 /**
285                  * Sets values of a subarray of wires. This takes up time, as specified by the
286                  * {@link WireArray}s travel time.
287                  * 
288                  * @param newValues   The new values the wires should take on.
289                  * @param startingBit The first index of the subarray of wires.
290                  * 
291                  * @author Fabian Stemmler
292                  */
293                 public void feedSignals(int startingBit, Bit... newValues)
294                 {
295                         Simulation.TIMELINE.addEvent((e) -> setValues(startingBit, newValues), travelTime);
296                 }
297
298                 private void setValues(int startingBit, Bit... newValues)
299                 {
300                         int exclLastIndex = startingBit + newValues.length;
301                         if (length < exclLastIndex)
302                                 throw new ArrayIndexOutOfBoundsException(String.format("Attempted to input bits from index %o to %o when there are only %o wires.", startingBit, exclLastIndex - 1, length));
303                         if (!Arrays.equals(inputValues, startingBit, exclLastIndex, newValues, 0, newValues.length))
304                         {
305                                 System.arraycopy(newValues, 0, inputValues, startingBit, newValues.length);
306                                 owner.recalculate();
307                         }
308                 }
309
310                 /**
311                  * Returns a copy (safe to modify) of the values the {@link WireArrayInput} is currently feeding into the associated {@link WireArray}.
312                  */
313                 public Bit[] getValues()
314                 {
315                         return inputValues.clone();
316                 }
317
318                 /**
319                  * {@link WireArrayInput} 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 (WireArrayInput wai : inputs) 
330                         {
331                                 if(wai == this)
332                                         continue;
333                                 Util.combineInto(bits, wai.getValues());
334                         }
335                         return bits;
336                 }
337                 
338                 @Override
339                 public String toString()
340                 {
341                         return Arrays.toString(inputValues);
342                 }
343         }
344
345         @Override
346         public String toString()
347         {
348                 return String.format("wire 0x%08x value: %s inputs: %s", hashCode(), Arrays.toString(values), inputs);
349         }
350
351         public static WireArrayInput[] extractInputs(WireArray[] w)
352         {
353                 WireArrayInput[] inputs = new WireArrayInput[w.length];
354                 for (int i = 0; i < w.length; i++)
355                         inputs[i] = w[i].createInput();
356                 return inputs;
357         }
358 }