WireEnd functionality split into ReadEnd and ReadWriteEnd
[Mograsim.git] / era.mi / src / era / mi / logic / wires / Wire.java
index 4c24571..9dba1fe 100644 (file)
@@ -1,13 +1,15 @@
 package era.mi.logic.wires;
 
+import static era.mi.logic.types.Bit.U;
+import static era.mi.logic.types.Bit.Z;
+
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
 import java.util.List;
 
-import era.mi.logic.Bit;
 import era.mi.logic.Simulation;
-import era.mi.logic.Util;
+import era.mi.logic.types.Bit;
+import era.mi.logic.types.BitVector;
+import era.mi.logic.types.BitVector.BitVectorMutator;
 
 /**
  * Represents an array of wires that can store n bits of information.
@@ -17,11 +19,11 @@ import era.mi.logic.Util;
  */
 public class Wire
 {
-       private Bit[] values;
+       private BitVector values;
        public final int travelTime;
-       private List<WireObserver> observers = new ArrayList<WireObserver>();
+       private List<ReadEnd> attached = new ArrayList<ReadEnd>();
        public final int length;
-       private List<WireEnd> inputs = new ArrayList<WireEnd>();
+       private List<ReadWriteEnd> inputs = new ArrayList<ReadWriteEnd>();
 
        public Wire(int length, int travelTime)
        {
@@ -35,41 +37,29 @@ public class Wire
 
        private void initValues()
        {
-               values = Bit.U.makeArray(length);
+               values = U.toVector(length);
        }
 
        private void recalculateSingleInput()
        {
-               WireEnd input = inputs.get(0);
-               if (!Arrays.equals(input.getInputValues(), values))
-               {
-                       Bit[] oldValues = values.clone();
-                       System.arraycopy(input.getInputValues(), 0, values, 0, length);
-                       notifyObservers(oldValues);
-               }
+               setNewValues(inputs.get(0).getInputValues());
        }
 
        private void recalculateMultipleInputs()
        {
-               Iterator<WireEnd> it = inputs.iterator();
-               Bit[] newValues = it.next().inputValues.clone();
-
-               while (it.hasNext())
-               {
-                       WireEnd input = it.next();
-                       Bit[] bits = input.getInputValues();
-                       for (int i = 0; i < length; i++)
-                       {
-                               newValues[i] = newValues[i].combineWith(bits[i]);
-                       }
-               }
+               BitVectorMutator mutator = BitVectorMutator.empty();
+               for (ReadWriteEnd wireArrayEnd : inputs)
+                       mutator.join(wireArrayEnd.getInputValues());
+               setNewValues(mutator.get());
+       }
 
-               if (!Arrays.equals(newValues, values))
-               {
-                       Bit[] oldValues = values;
-                       values = newValues;
-                       notifyObservers(oldValues);
-               }
+       private void setNewValues(BitVector newValues)
+       {
+               if (values.equals(newValues))
+                       return;
+               BitVector oldValues = values;
+               values = newValues;
+               notifyObservers(oldValues);
        }
 
        private void recalculate()
@@ -115,15 +105,14 @@ public class Wire
        {
                long val = 0;
                long mask = 1;
-               for (int i = 0; i < length; i++)
+               for (Bit bit : values)
                {
-                       switch (values[i])
+                       switch (bit)
                        {
                        default:
                        case Z:
                        case X:
                                return 0; // TODO: Proper handling for getUnsignedValue(), if not all bits are 1 or 0;
-                       // Random number?
                        case ONE:
                                val |= mask;
                                break;
@@ -160,20 +149,17 @@ public class Wire
 
        public Bit getValue(int index)
        {
-               return values[index];
+               return values.getBit(index);
        }
 
-       public Bit[] getValues(int start, int end)
+       public BitVector getValues(int start, int end)
        {
-               int length = end - start;
-               Bit[] bits = new Bit[length];
-               System.arraycopy(values, start, bits, 0, length);
-               return bits;
+               return values.subVector(start, end);
        }
 
-       public Bit[] getValues()
+       public BitVector getValues()
        {
-               return values.clone();
+               return values;
        }
 
        /**
@@ -184,149 +170,64 @@ public class Wire
         * 
         * @author Fabian Stemmler
         */
-       public boolean addObserver(WireObserver ob)
+       private void attachEnd(ReadEnd end)
+       {
+               attached.add(end);
+       }
+
+       private void detachEnd(ReadEnd end)
        {
-               return observers.add(ob);
+               attached.remove(end);
        }
 
-       private void notifyObservers(Bit[] oldValues)
+       private void notifyObservers(BitVector oldValues)
        {
-               for (WireObserver o : observers)
-                       o.update(this, oldValues);
+               for (ReadEnd o : attached)
+                       o.update(oldValues);
        }
 
        /**
-        * Create and register a {@link WireEnd} object, which is tied to this {@link Wire}.
+        * Create and register a {@link ReadWriteEnd} object, which is tied to this {@link Wire}. This {@link ReadWriteEnd} can be written to.
         */
-       public WireEnd createEnd()
+       public ReadWriteEnd createEnd()
        {
-               return new WireEnd();
+               return new ReadWriteEnd();
        }
 
-       private void registerInput(WireEnd toRegister)
+       /**
+        * Create a {@link ReadEnd} object, which is tied to this {@link Wire}. This {@link ReadEnd} cannot be written to.
+        */
+       public ReadEnd createReadOnlyEnd()
+       {
+               return new ReadEnd();
+       }
+
+       private void registerInput(ReadWriteEnd toRegister)
        {
                inputs.add(toRegister);
        }
 
        /**
-        * A {@link WireEnd} feeds a constant signal into the {@link Wire} it is tied to. The combination of all inputs determines the
+        * A {@link ReadEnd} feeds a constant signal into the {@link Wire} it is tied to. The combination of all inputs determines the
         * {@link Wire}s final value. X dominates all other inputs Z does not affect the final value, unless there are no other inputs than Z 0
         * and 1 turn into X when they are mixed
         * 
         * @author Fabian Stemmler
         */
-       public class WireEnd
+       public class ReadEnd
        {
-               private boolean open;
-               private Bit[] inputValues;
+               private List<WireObserver> observers = new ArrayList<WireObserver>();
 
-               private WireEnd()
+               private ReadEnd()
                {
                        super();
-                       open = true;
-                       initValues();
-                       registerInput(this);
+                       Wire.this.attachEnd(this);
                }
 
-               private void initValues()
+               public void update(BitVector oldValues)
                {
-                       inputValues = Bit.Z.makeArray(length);
-               }
-
-               /**
-                * Sets the wires values. This takes up time, as specified by the {@link Wire}s travel time.
-                * 
-                * @param newValues The new values the wires should take on.
-                * 
-                * @author Fabian Stemmler
-                */
-               public void feedSignals(Bit... newValues)
-               {
-                       if (newValues.length != length)
-                               throw new IllegalArgumentException(
-                                               String.format("Attempted to input %d bits instead of %d bits.", newValues.length, length));
-                       feedSignals(0, newValues);
-               }
-
-               /**
-                * Sets values of a subarray of wires. This takes up time, as specified by the {@link Wire}s travel time.
-                * 
-                * @param newValues   The new values the wires should take on.
-                * @param startingBit The first index of the subarray of wires.
-                * 
-                * @author Fabian Stemmler
-                */
-               public void feedSignals(int startingBit, Bit... newValues)
-               {
-                       if (!open)
-                               throw new RuntimeException("Attempted to write to closed WireArrayEnd.");
-                       Simulation.TIMELINE.addEvent((e) -> setValues(startingBit, newValues), travelTime);
-               }
-
-               private void setValues(int startingBit, Bit... newValues)
-               {
-                       int exclLastIndex = startingBit + newValues.length;
-                       if (length < exclLastIndex)
-                               throw new ArrayIndexOutOfBoundsException(
-                                               String.format("Attempted to input bits from index %d to %d when there are only %d wires.", startingBit,
-                                                               exclLastIndex - 1, length));
-                       if (!Arrays.equals(inputValues, startingBit, exclLastIndex, newValues, 0, newValues.length))
-                       {
-                               System.arraycopy(newValues, 0, inputValues, startingBit, newValues.length);
-                               Wire.this.recalculate();
-                       }
-               }
-
-               /**
-                * @return The value (of bit 0) the {@link WireEnd} is currently feeding into the associated {@link Wire}.
-                */
-               public Bit getInputValue()
-               {
-                       return getInputValue(0);
-               }
-
-               /**
-                * @return The value which the {@link WireEnd} is currently feeding into the associated {@link Wire} at the indexed {@link Bit}.
-                */
-               public Bit getInputValue(int index)
-               {
-                       return inputValues[index];
-               }
-
-               /**
-                * @return A copy (safe to modify) of the values the {@link WireEnd} is currently feeding into the associated {@link Wire}.
-                */
-               public Bit[] getInputValues()
-               {
-                       return getInputValues(0, length);
-               }
-
-               public Bit[] getInputValues(int start, int end)
-               {
-                       int length = end - start;
-                       Bit[] bits = new Bit[length];
-                       System.arraycopy(inputValues, start, bits, 0, length);
-                       return bits;
-               }
-
-               /**
-                * {@link WireEnd} now feeds Z into the associated {@link Wire}.
-                */
-               public void clearSignals()
-               {
-                       feedSignals(Bit.Z.makeArray(length));
-               }
-
-               public Bit[] wireValuesExcludingMe()
-               {
-                       Bit[] bits = Bit.Z.makeArray(length);
-                       for (WireEnd wai : inputs)
-                       {
-                               if (wai == this)
-                                       continue;
-                               Util.combineInto(bits, wai.getInputValues());
-                       }
-                       return bits;
+                       for (WireObserver ob : observers)
+                               ob.update(this, oldValues);
                }
 
                /**
@@ -358,7 +259,7 @@ public class Wire
                 * 
                 * @author Fabian Stemmler
                 */
-               public Bit[] getValues()
+               public BitVector getValues()
                {
                        return Wire.this.getValues();
                }
@@ -370,7 +271,7 @@ public class Wire
                 * 
                 * @author Fabian Stemmler
                 */
-               public Bit[] getValues(int start, int end)
+               public BitVector getValues(int start, int end)
                {
                        return Wire.this.getValues(start, end);
                }
@@ -415,14 +316,14 @@ public class Wire
                @Override
                public String toString()
                {
-                       return Arrays.toString(values);
-                       // return String.format("%s \nFeeding: %s", WireArray.this.toString(), Arrays.toString(inputValues));
+                       return Wire.this.toString();
                }
 
-               public void disconnect()
+               public void close()
                {
                        inputs.remove(this);
-                       open = false;
+                       detachEnd(this);
+                       recalculate();
                }
 
                public int length()
@@ -432,7 +333,7 @@ public class Wire
 
                public boolean addObserver(WireObserver ob)
                {
-                       return Wire.this.addObserver(ob);
+                       return observers.add(ob);
                }
 
                public Wire getWire()
@@ -441,16 +342,147 @@ public class Wire
                }
        }
 
+       public class ReadWriteEnd extends ReadEnd
+       {
+               private boolean open;
+               private BitVector inputValues;
+
+               private ReadWriteEnd()
+               {
+                       super();
+                       open = true;
+                       initValues();
+                       registerInput(this);
+               }
+
+               private void initValues()
+               {
+                       inputValues = U.toVector(length);
+               }
+
+               /**
+                * Sets the wires values. This takes up time, as specified by the {@link Wire}s travel time.
+                * 
+                * @param newValues The new values the wires should take on.
+                * 
+                * @author Fabian Stemmler
+                */
+               public void feedSignals(Bit... newValues)
+               {
+                       feedSignals(BitVector.of(newValues));
+               }
+
+               public void feedSignals(BitVector newValues)
+               {
+                       if (newValues.length() != length)
+                               throw new IllegalArgumentException(
+                                               String.format("Attempted to input %d bits instead of %d bits.", newValues.length(), length));
+                       if (!open)
+                               throw new RuntimeException("Attempted to write to closed WireArrayEnd.");
+                       Simulation.TIMELINE.addEvent(e -> setValues(newValues), travelTime);
+               }
+
+               /**
+                * Sets values of a subarray of wires. This takes up time, as specified by the {@link Wire}s travel time.
+                * 
+                * @param bitVector   The new values the wires should take on.
+                * @param startingBit The first index of the subarray of wires.
+                * 
+                * @author Fabian Stemmler
+                */
+               public void feedSignals(int startingBit, BitVector bitVector)
+               {
+                       if (!open)
+                               throw new RuntimeException("Attempted to write to closed WireArrayEnd.");
+                       Simulation.TIMELINE.addEvent(e -> setValues(startingBit, bitVector), travelTime);
+               }
+
+               private void setValues(int startingBit, BitVector newValues)
+               {
+                       // index check covered in equals
+                       if (!inputValues.equalsWithOffset(newValues, startingBit))
+                       {
+                               Bit[] vals = inputValues.getBits();
+                               System.arraycopy(newValues.getBits(), 0, vals, startingBit, newValues.length());
+                               inputValues = BitVector.of(vals);
+                               Wire.this.recalculate();
+                       }
+               }
+
+               private void setValues(BitVector newValues)
+               {
+                       if (inputValues.equals(newValues))
+                               return;
+                       inputValues = newValues;
+                       Wire.this.recalculate();
+               }
+
+               /**
+                * @return The value (of bit 0) the {@link ReadEnd} is currently feeding into the associated {@link Wire}.
+                */
+               public Bit getInputValue()
+               {
+                       return getInputValue(0);
+               }
+
+               /**
+                * @return The value which the {@link ReadEnd} is currently feeding into the associated {@link Wire} at the indexed {@link Bit}.
+                */
+               public Bit getInputValue(int index)
+               {
+                       return inputValues.getBit(index);
+               }
+
+               /**
+                * @return A copy (safe to modify) of the values the {@link ReadEnd} is currently feeding into the associated {@link Wire}.
+                */
+               public BitVector getInputValues()
+               {
+                       return getInputValues(0, length);
+               }
+
+               public BitVector getInputValues(int start, int end)
+               {
+                       return inputValues.subVector(start, end);
+               }
+
+               /**
+                * {@link ReadEnd} now feeds Z into the associated {@link Wire}.
+                */
+               public void clearSignals()
+               {
+                       feedSignals(Z.toVector(length));
+               }
+
+               public BitVector wireValuesExcludingMe()
+               {
+                       BitVectorMutator mutator = BitVectorMutator.empty();
+                       for (ReadWriteEnd wireEnd : inputs)
+                       {
+                               if (wireEnd == this)
+                                       continue;
+                               mutator.join(wireEnd.inputValues);
+                       }
+                       return mutator.get();
+               }
+
+               @Override
+               public String toString()
+               {
+                       return inputValues.toString();
+               }
+       }
+
        @Override
        public String toString()
        {
-               return String.format("wire 0x%08x value: %s inputs: %s", hashCode(), Arrays.toString(values), inputs);
+               return String.format("wire 0x%08x value: %s inputs: %s", hashCode(), values, inputs);
                // Arrays.toString(values), inputs.stream().map(i -> Arrays.toString(i.inputValues)).reduce((s1, s2) -> s1 + s2)
        }
 
-       public static WireEnd[] extractEnds(Wire[] w)
+       public static ReadEnd[] extractEnds(Wire[] w)
        {
-               WireEnd[] inputs = new WireEnd[w.length];
+               ReadEnd[] inputs = new ReadEnd[w.length];
                for (int i = 0; i < w.length; i++)
                        inputs[i] = w[i].createEnd();
                return inputs;