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