1 /*
2  * Copyright (c) 2016 Helmut Neemann
3  * Use of this source code is governed by the GPL v3 license
4  * that can be found in the LICENSE file.
5  */
6 package de.neemann.digital.core;
7 
8 import de.neemann.digital.core.element.ElementTypeDescription;
9 import de.neemann.digital.core.element.PinDescription;
10 import de.neemann.digital.lang.Lang;
11 
12 import java.util.Random;
13 
14 /**
15  * Represents all signal values in the simulator.
16  * There are some setters to set the value. Each bit of a value can be set to high z state.
17  * Observers can observe this value to be notified if the value changes.
18  */
19 public class ObservableValue extends Observable implements PinDescription {
20 
21     private final String name;
22     private final long mask;
23     private final long signedFlag;
24     private final int bits;
25     // the value, high z bits are always set to zero
26     private long value;
27     // the high z state of each bit
28     private long highZ;
29     private boolean bidirectional;
30     private boolean isConstant = false;
31     private String description;
32     private String pinNumber;
33     private boolean isSwitchPin;
34     // used to create random bits if high-z values are read
35     private Random random;
36 
37     /**
38      * Creates a new instance.
39      *
40      * @param name the name of this value
41      * @param bits the number of bits
42      */
ObservableValue(String name, int bits)43     public ObservableValue(String name, int bits) {
44         this.name = name;
45         this.bits = bits;
46         mask = Bits.mask(bits);
47         signedFlag = Bits.signedFlagMask(bits);
48     }
49 
50 
51     /**
52      * Makes this value a constant value
53      *
54      * @return this for chained calls
55      */
setConstant()56     public ObservableValue setConstant() {
57         isConstant = true;
58         return this;
59     }
60 
61     /**
62      * @return true if this value is a constant
63      */
isConstant()64     public boolean isConstant() {
65         return isConstant;
66     }
67 
68     /**
69      * Sets the value and fires an event if the value has changed.
70      * Also sets all bits to low Z.
71      *
72      * @param value the new value
73      * @return this for chained calls
74      */
setValue(long value)75     public ObservableValue setValue(long value) {
76         return set(value, 0);
77     }
78 
79     /**
80      * Sets all bits to highZ state.
81      *
82      * @return this for chained calls
83      */
setToHighZ()84     public ObservableValue setToHighZ() {
85         return set(0, -1);
86     }
87 
88     /**
89      * Sets the value and highZ state and fires an event if value has changed.
90      *
91      * @param value the value
92      * @param highZ highZ state
93      * @return this for chained calls
94      */
set(long value, long highZ)95     public ObservableValue set(long value, long highZ) {
96         value = getValueBits(value);
97         highZ = getValueBits(highZ);
98         if (highZ != this.highZ || ((~highZ & (value ^ this.value))) != 0) {
99 
100             if (isConstant)
101                 throw new RuntimeException("tried to modify a constant value!");
102 
103             this.highZ = highZ;
104             this.value = value & (~highZ);  // high Z bits are set to zero
105             fireHasChanged();
106         }
107         return this;
108     }
109 
110     /**
111      * Adds an observer to this value.
112      *
113      * @param observer the observer to add
114      * @return this for chained calls
115      */
addObserverToValue(Observer observer)116     public ObservableValue addObserverToValue(Observer observer) {
117         addObserver(observer);
118         return this;
119     }
120 
121     /**
122      * @return the number of bits of this value
123      */
getBits()124     public int getBits() {
125         return bits;
126     }
127 
128     /**
129      * Returns the current value.
130      * The high-z bits are set to a random value.
131      *
132      * @return the value
133      */
getValue()134     public long getValue() {
135         if (highZ != 0) {
136             if (random == null)
137                 random = new Random();
138             return value | (random.nextLong() & highZ);
139         } else
140             return value;
141     }
142 
143     /**
144      * Returns the current value
145      * The high-z bits are set to zero
146      *
147      * @return the value
148      */
getValueHighZIsZero()149     public long getValueHighZIsZero() {
150         return value;
151     }
152 
153     /**
154      * returns the actual high z bit mask
155      *
156      * @return the high z bit mask
157      */
getHighZ()158     public long getHighZ() {
159         return highZ;
160     }
161 
162     /**
163      * returns the actual value as a string
164      *
165      * @return the value as string
166      */
getValueString()167     public String getValueString() {
168         if (highZ != 0)
169             if (highZ == mask)
170                 return "Z";
171             else {
172                 return zMaskString(value, highZ, bits);
173             }
174         else {
175             return IntFormat.toShortHex(value);
176         }
177     }
178 
zMaskString(long value, long highZ, int bits)179     static String zMaskString(long value, long highZ, int bits) {
180         StringBuilder sb = new StringBuilder();
181         long m = Bits.up(1, bits - 1);
182         for (int i = 0; i < bits; i++) {
183             if ((highZ & m) != 0) {
184                 sb.append("z");
185             } else {
186                 if ((value & m) != 0) {
187                     sb.append("1");
188                 } else {
189                     sb.append("0");
190                 }
191             }
192             m >>>= 1;
193         }
194         return sb.toString();
195     }
196 
197     /**
198      * returns the actual value as a signed long
199      *
200      * @return the signed value
201      */
getValueSigned()202     public long getValueSigned() {
203         long v = getValue();
204         if ((v & signedFlag) != 0) v |= ~mask;
205         return v;
206     }
207 
208     /**
209      * Returns the value as a bool.
210      *
211      * @return the boolean
212      */
getBool()213     public boolean getBool() {
214         return getValue() != 0;
215     }
216 
217     /**
218      * Sets a bool value.
219      *
220      * @param bool the boolean to set
221      */
setBool(boolean bool)222     public void setBool(boolean bool) {
223         if (bool)
224             setValue(1);
225         else
226             setValue(0);
227     }
228 
229     /**
230      * reduces a given value to the number of bits used by this value.
231      *
232      * @param value the value to reduce
233      * @return the reduced value
234      */
getValueBits(long value)235     public long getValueBits(long value) {
236         return value & mask;
237     }
238 
239     /**
240      * checks if the given number of bits is the same used by this value.
241      * It is a convenience method to make this check simpler to code.
242      *
243      * @param bits the number of bits
244      * @param node the node to add to the exception if one is thrown
245      * @return this for chained calls
246      * @throws BitsException thrown if bit numbers do not match
247      */
checkBits(int bits, Node node)248     public ObservableValue checkBits(int bits, Node node) throws BitsException {
249         return checkBits(bits, node, -1);
250     }
251 
252     /**
253      * checks if the given number of bits is the same used by this value.
254      * It is a convenience method to make this check simpler to code.
255      *
256      * @param bits  the number of bits
257      * @param node  the node to add to the exception if one is thrown
258      * @param input the affected nodes input
259      * @return this for chained calls
260      * @throws BitsException thrown if bit numbers do not match
261      */
checkBits(int bits, Node node, int input)262     public ObservableValue checkBits(int bits, Node node, int input) throws BitsException {
263         if (this.bits != bits) {
264             throw new BitsException(Lang.get("err_needs_N0_bits_found_N2_bits", bits, this.bits), node, input, this);
265         }
266         return this;
267     }
268 
269     /**
270      * @return true if one of the bits is in high z state
271      */
isHighZ()272     public boolean isHighZ() {
273         return highZ != 0;
274     }
275 
276     @Override
toString()277     public String toString() {
278         return name + "{"
279                 + "value=" + getValueString()
280                 + ", setBits=" + bits
281                 + '}';
282     }
283 
284     /**
285      * @return the name of this value
286      */
getName()287     public String getName() {
288         return name;
289     }
290 
291     /**
292      * Makes this value a bidirectional value.
293      *
294      * @return this for chained calls
295      */
setBidirectional()296     public ObservableValue setBidirectional() {
297         this.bidirectional = true;
298         return this;
299     }
300 
301     @Override
getDescription()302     public String getDescription() {
303         if (description != null)
304             return description;
305         else
306             return getName();
307     }
308 
309     /**
310      * Sets the description of this value.
311      *
312      * @param description the description
313      * @return this for call chaining
314      */
setDescription(String description)315     public ObservableValue setDescription(String description) {
316         this.description = description;
317         return this;
318     }
319 
320     @Override
getDirection()321     public Direction getDirection() {
322         if (bidirectional)
323             return Direction.both;
324         else
325             return Direction.output;
326     }
327 
328     /**
329      * Returns a list containing only this {@link ObservableValue}
330      *
331      * @return the list
332      */
asList()333     public ObservableValues asList() {
334         return new ObservableValues(this);
335     }
336 
337     /**
338      * sets this description to the appropriate language entry which is determined by the
339      * given descriptions language key.
340      *
341      * @param description the {@link ElementTypeDescription}
342      * @return this for chained calls
343      */
setPinDescription(ElementTypeDescription description)344     public ObservableValue setPinDescription(ElementTypeDescription description) {
345         setDescription(Lang.get(description.getPinLangKey() + name));
346         return this;
347     }
348 
349     @Override
getPinNumber()350     public String getPinNumber() {
351         return pinNumber;
352     }
353 
354     @Override
isClock()355     public boolean isClock() {
356         return false;  // output pins are never clock pins
357     }
358 
359     @Override
isSwitchPin()360     public boolean isSwitchPin() {
361         return isSwitchPin;
362     }
363 
364     /**
365      * Flags this output value as a switch output
366      *
367      * @param switchPin true is switch pin
368      * @return this for chained calls
369      */
setSwitchPin(boolean switchPin)370     public ObservableValue setSwitchPin(boolean switchPin) {
371         isSwitchPin = switchPin;
372         return this;
373     }
374 
375     /**
376      * Sets the pin number
377      *
378      * @param pinNumber the pin number
379      * @return this for chained calls
380      */
setPinNumber(String pinNumber)381     public ObservableValue setPinNumber(String pinNumber) {
382         this.pinNumber = pinNumber;
383         return this;
384     }
385 
386     /**
387      * @return a copy of this value
388      */
getCopy()389     public Value getCopy() {
390         return new Value(this);
391     }
392 }
393