1 /* 2 * Copyright (c) 2018 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.ElementAttributes; 9 import de.neemann.digital.core.element.Keys; 10 11 /** 12 * The available number formats 13 */ 14 public enum IntFormat { 15 /** 16 * the default format 17 */ 18 def(ValueFormatterDefault.INSTANCE), 19 /** 20 * decimal 21 */ 22 dec(new ValueFormatterDecimal(false)), 23 /** 24 * decimal signed 25 */ 26 decSigned(new ValueFormatterDecimal(true)), 27 /** 28 * hex 29 */ 30 hex(ValueFormatterHex.INSTANCE), 31 /** 32 * binary 33 */ 34 bin(new ValueFormatterBinary()), 35 /** 36 * octal 37 */ 38 oct(new ValueFormatterOctal()), 39 /** 40 * ascii 41 */ 42 ascii(new ValueFormatterAscii()), 43 /** 44 * fixed point 45 */ 46 fixed(attributes -> new ValueFormatterFixedPoint(attributes, false)), 47 /** 48 * fixed point signed 49 */ 50 fixedSigned(attributes -> new ValueFormatterFixedPoint(attributes, true)), 51 /** 52 * floating point 53 */ 54 floating(new ValueFormatterFloat()); 55 56 /** 57 * The default formatter 58 */ 59 public static final ValueFormatter DEFAULT_FORMATTER = ValueFormatterDefault.INSTANCE; 60 /** 61 * The hexadecimal formatter 62 */ 63 public static final ValueFormatter HEX_FORMATTER = ValueFormatterHex.INSTANCE; 64 65 private final Factory factory; 66 private final boolean dependsOnAttributes; 67 IntFormat(ValueFormatter instance)68 IntFormat(ValueFormatter instance) { 69 this.factory = attributes -> instance; 70 this.dependsOnAttributes = false; 71 } 72 IntFormat(Factory factory)73 IntFormat(Factory factory) { 74 this.factory = factory; 75 this.dependsOnAttributes = true; 76 } 77 78 /** 79 * Creates a formatter which is able to format Values 80 * 81 * @param attributes the elements attributes 82 * @return the created {@link ValueFormatter} 83 */ createFormatter(ElementAttributes attributes)84 public ValueFormatter createFormatter(ElementAttributes attributes) { 85 return factory.create(attributes); 86 } 87 88 /** 89 * @return true if this type depends on elements attributes 90 */ dependsOnAttributes()91 public boolean dependsOnAttributes() { 92 return dependsOnAttributes; 93 } 94 95 private interface Factory { create(ElementAttributes attributes)96 ValueFormatter create(ElementAttributes attributes); 97 } 98 99 private static final char[] DIGITS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 100 101 /** 102 * The default value formatter 103 */ 104 private static final class ValueFormatterDefault implements ValueFormatter { 105 private static final ValueFormatter INSTANCE = new ValueFormatterDefault(); 106 107 @Override formatToView(Value inValue)108 public String formatToView(Value inValue) { 109 if (inValue.isHighZ()) 110 return inValue.toString(); 111 else 112 return toShortHex(inValue.getValue(), false); 113 } 114 115 @Override formatToEdit(Value inValue)116 public String formatToEdit(Value inValue) { 117 if (inValue.isHighZ()) 118 return "Z"; 119 120 final long value = inValue.getValue(); 121 if (value >= 0 && value < 10) 122 return Long.toString(value); 123 else 124 return "0x" + toShortHex(value, true); 125 } 126 127 @Override strLen(int bits)128 public int strLen(int bits) { 129 return (bits - 1) / 4 + 3; 130 } 131 132 @Override isSuitedForAddresses()133 public boolean isSuitedForAddresses() { 134 return false; // difficult to read in a table 135 } 136 137 @Override dragValue(long initial, int bits, double inc)138 public long dragValue(long initial, int bits, double inc) { 139 return dragValueSigned(initial, bits, inc, false); 140 } 141 142 @Override isSeparatorInFrontOf(int bits, int bit)143 public boolean isSeparatorInFrontOf(int bits, int bit) { 144 return bit % 4 == 0; 145 } 146 } 147 dragValueSigned(long initial, int bits, double inc, boolean signed)148 private static long dragValueSigned(long initial, int bits, double inc, boolean signed) { 149 long max; 150 long min; 151 if (signed) { 152 long mask = Bits.mask(bits); 153 long signedFlag = Bits.signedFlagMask(bits); 154 if ((initial & signedFlag) != 0) initial |= ~mask; 155 156 max = mask >>> 1; 157 min = -max - 1; 158 } else { 159 max = Bits.mask(bits); 160 min = 0; 161 } 162 return Math.max(min, Math.min(max, initial + Math.round(max * inc))); 163 } 164 165 /** 166 * Base class of all formatters where the string to edit and the string to display are the same. 167 */ 168 private static abstract class ValueFormatterViewEdit implements ValueFormatter { 169 private final boolean suitedForAddresses; 170 ValueFormatterViewEdit(boolean suitedForAddresses)171 private ValueFormatterViewEdit(boolean suitedForAddresses) { 172 this.suitedForAddresses = suitedForAddresses; 173 } 174 175 @Override formatToView(Value inValue)176 public String formatToView(Value inValue) { 177 if (inValue.isHighZ()) 178 return inValue.toString(); 179 else 180 return format(inValue); 181 } 182 183 @Override formatToEdit(Value inValue)184 public String formatToEdit(Value inValue) { 185 if (inValue.isHighZ()) 186 return "Z"; 187 else 188 return format(inValue); 189 } 190 191 @Override isSuitedForAddresses()192 public boolean isSuitedForAddresses() { 193 return suitedForAddresses; 194 } 195 format(Value value)196 protected abstract String format(Value value); 197 198 @Override dragValue(long initial, int bits, double inc)199 public long dragValue(long initial, int bits, double inc) { 200 return dragValueSigned(initial, bits, inc, false); 201 } 202 } 203 204 /** 205 * the hexadecimal formatter 206 */ 207 private static final class ValueFormatterHex extends ValueFormatterViewEdit { 208 private static final ValueFormatterHex INSTANCE = new ValueFormatterHex(); 209 ValueFormatterHex()210 private ValueFormatterHex() { 211 super(true); 212 } 213 214 @Override format(Value inValue)215 protected String format(Value inValue) { 216 final int bits = inValue.getBits(); 217 final int numChars = (bits - 1) / 4 + 1; 218 219 StringBuilder sb = new StringBuilder("0x"); 220 final long value = inValue.getValue(); 221 for (int i = numChars - 1; i >= 0; i--) { 222 int c = (int) ((value >> (i * 4)) & 0xf); 223 sb.append(DIGITS[c]); 224 } 225 return sb.toString(); 226 } 227 228 @Override strLen(int bits)229 public int strLen(int bits) { 230 return (bits - 1) / 4 + 3; 231 } 232 233 @Override isSeparatorInFrontOf(int bits, int bit)234 public boolean isSeparatorInFrontOf(int bits, int bit) { 235 return bit % 4 == 0; 236 } 237 } 238 239 /** 240 * Creates a short hex representation of the given value. 241 * Use only to represent a value. 242 * If confusion is excluded, the prefix '0x' is omitted. 243 * Thus 0x1A3 is converted to "1A3" which can not be parsed back to a long because "0x" is missing. 244 * 245 * @param value the value 246 * @return the hex string 247 */ toShortHex(long value)248 public static String toShortHex(long value) { 249 return toShortHex(value, false); 250 } 251 252 private static final int BUF = 16; 253 toShortHex(long value, boolean omitPrefix)254 private static String toShortHex(long value, boolean omitPrefix) { 255 if (value == 0) 256 return "0"; 257 258 boolean wasChar = false; 259 int p = BUF; 260 char[] data = new char[BUF]; 261 while (value != 0) { 262 final int d = (int) (value & 0xf); 263 if (d >= 10) wasChar = true; 264 p--; 265 data[p] = DIGITS[d]; 266 value >>>= 4; 267 } 268 269 if (omitPrefix || wasChar || p == BUF - 1) 270 return new String(data, p, BUF - p); 271 else 272 return "0x" + new String(data, p, BUF - p); 273 } 274 275 /** 276 * the octal formatter 277 */ 278 private static final class ValueFormatterOctal extends ValueFormatterViewEdit { 279 ValueFormatterOctal()280 private ValueFormatterOctal() { 281 super(true); 282 } 283 284 @Override strLen(int bits)285 public int strLen(int bits) { 286 return (bits - 1) / 3 + 3; 287 } 288 289 @Override format(Value inValue)290 protected String format(Value inValue) { 291 final int bits = inValue.getBits(); 292 final int numChars = (bits - 1) / 3 + 1; 293 294 StringBuilder sb = new StringBuilder("0"); 295 final long value = inValue.getValue(); 296 for (int i = numChars - 1; i >= 0; i--) { 297 int c = (int) ((value >> (i * 3)) & 0x7); 298 sb.append(DIGITS[c]); 299 } 300 return sb.toString(); 301 } 302 303 @Override isSeparatorInFrontOf(int bits, int bit)304 public boolean isSeparatorInFrontOf(int bits, int bit) { 305 return bit % 3 == 0; 306 } 307 } 308 309 /** 310 * the binary formatter 311 */ 312 private static final class ValueFormatterBinary extends ValueFormatterViewEdit { 313 ValueFormatterBinary()314 private ValueFormatterBinary() { 315 super(false); // column becomes to wide 316 } 317 318 @Override strLen(int bits)319 public int strLen(int bits) { 320 return bits + 2; 321 } 322 323 @Override format(Value inValue)324 protected String format(Value inValue) { 325 final int bits = inValue.getBits(); 326 char[] data = new char[bits]; 327 final long value = inValue.getValue(); 328 long mask = 1; 329 for (int i = bits - 1; i >= 0; i--) { 330 if ((value & mask) != 0) 331 data[i] = '1'; 332 else 333 data[i] = '0'; 334 mask <<= 1; 335 } 336 return "0b" + new String(data); 337 } 338 } 339 340 /** 341 * The ascii formatter 342 */ 343 private static final class ValueFormatterAscii extends ValueFormatterViewEdit { 344 ValueFormatterAscii()345 private ValueFormatterAscii() { 346 super(false); // does not represent all values 347 } 348 349 @Override strLen(int bits)350 public int strLen(int bits) { 351 return 3; 352 } 353 354 @Override format(Value value)355 protected String format(Value value) { 356 return "'" + ((char) value.getValue()) + "'"; 357 } 358 } 359 360 /** 361 * The decimal value formatter 362 */ 363 private static final class ValueFormatterDecimal extends ValueFormatterViewEdit { 364 private final boolean signed; 365 ValueFormatterDecimal(boolean signed)366 private ValueFormatterDecimal(boolean signed) { 367 super(true); 368 this.signed = signed; 369 } 370 371 @Override strLen(int bits)372 public int strLen(int bits) { 373 if (signed) 374 return decStrLen(bits - 1) + 1; 375 else 376 return decStrLen(bits); 377 } 378 379 @Override format(Value value)380 protected String format(Value value) { 381 if (signed) 382 return Long.toString(value.getValueSigned()); 383 else 384 return Long.toString(value.getValue()); 385 } 386 387 @Override dragValue(long initial, int bits, double inc)388 public long dragValue(long initial, int bits, double inc) { 389 return dragValueSigned(initial, bits, inc, signed); 390 } 391 } 392 decStrLen(int bits)393 private static int decStrLen(int bits) { 394 if (bits == 64) 395 return 20; 396 else if (bits == 63) { 397 return 19; 398 } else 399 return (int) Math.ceil(Math.log10(1L << bits)); 400 } 401 402 403 /** 404 * Fixed point formatter 405 */ 406 private static final class ValueFormatterFixedPoint implements ValueFormatter { 407 private static final int[] TABLE = new int[]{ 408 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14, 15, 16, 17, 17, 409 18, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 410 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 411 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22}; 412 413 private final int fixedPoint; 414 private final boolean signed; 415 private final double divisor; 416 417 /** 418 * Creates a new generic instance 419 * 420 * @param attr the defining elements attributes 421 * @param signed signed 422 */ ValueFormatterFixedPoint(ElementAttributes attr, boolean signed)423 private ValueFormatterFixedPoint(ElementAttributes attr, boolean signed) { 424 fixedPoint = attr.get(Keys.FIXED_POINT); 425 divisor = Bits.up(1, fixedPoint); 426 this.signed = signed; 427 } 428 429 @Override formatToView(Value inValue)430 public String formatToView(Value inValue) { 431 if (inValue.isHighZ()) 432 return inValue.toString(); 433 return format(inValue); 434 } 435 436 @Override formatToEdit(Value inValue)437 public String formatToEdit(Value inValue) { 438 if (inValue.isHighZ()) 439 return "Z"; 440 return format(inValue) + ":" + fixedPoint; 441 } 442 443 @Override strLen(int bits)444 public int strLen(int bits) { 445 int fp = fixedPoint; 446 if (fp >= TABLE.length) fp = TABLE.length - 1; 447 return decStrLen(Math.max(1, bits - fp)) + TABLE[fp]; 448 } 449 450 @Override isSuitedForAddresses()451 public boolean isSuitedForAddresses() { 452 return false; 453 } 454 format(Value inValue)455 private String format(Value inValue) { 456 if (signed) 457 return Double.toString(inValue.getValueSigned() / divisor); 458 else 459 return Double.toString(inValue.getValue() / divisor); 460 } 461 462 @Override dragValue(long initial, int bits, double inc)463 public long dragValue(long initial, int bits, double inc) { 464 return dragValueSigned(initial, bits, inc, signed); 465 } 466 467 @Override isSeparatorInFrontOf(int bits, int bit)468 public boolean isSeparatorInFrontOf(int bits, int bit) { 469 return bit == fixedPoint; 470 } 471 } 472 473 /** 474 * Floating point formatter 475 */ 476 private static final class ValueFormatterFloat implements ValueFormatter { 477 private static final int SIZE32 = Float.toString((float) -Math.PI).length(); 478 private static final int SIZE64 = Double.toString(-Math.PI).length(); 479 480 @Override formatToView(Value inValue)481 public String formatToView(Value inValue) { 482 if (inValue.isHighZ()) 483 return inValue.toString(); 484 485 switch (inValue.getBits()) { 486 case 32: 487 return Float.toString(Float.intBitsToFloat((int) inValue.getValue())); 488 case 64: 489 return Double.toString(Double.longBitsToDouble(inValue.getValue())); 490 default: 491 return HEX_FORMATTER.formatToView(inValue); 492 } 493 } 494 495 @Override formatToEdit(Value inValue)496 public String formatToEdit(Value inValue) { 497 if (inValue.isHighZ()) 498 return "Z"; 499 500 switch (inValue.getBits()) { 501 case 32: 502 float f = Float.intBitsToFloat((int) inValue.getValue()); 503 if (Float.isFinite(f)) 504 return Float.toString(f); 505 else 506 return HEX_FORMATTER.formatToEdit(inValue); 507 case 64: 508 double d = Double.longBitsToDouble(inValue.getValue()); 509 if (Double.isFinite(d)) 510 return d + "d"; 511 else 512 return HEX_FORMATTER.formatToEdit(inValue); 513 default: 514 return HEX_FORMATTER.formatToEdit(inValue); 515 } 516 } 517 518 @Override strLen(int bits)519 public int strLen(int bits) { 520 switch (bits) { 521 case 32: 522 return SIZE32; 523 case 64: 524 return SIZE64; 525 default: 526 return HEX_FORMATTER.strLen(bits); 527 } 528 } 529 530 @Override isSuitedForAddresses()531 public boolean isSuitedForAddresses() { 532 return false; 533 } 534 535 @Override dragValue(long initialInt, int bits, double inc)536 public long dragValue(long initialInt, int bits, double inc) { 537 double initial; 538 if (bits == 32) 539 initial = Float.intBitsToFloat((int) initialInt); 540 else if (bits == 64) 541 initial = Double.longBitsToDouble(initialInt); 542 else 543 return HEX_FORMATTER.dragValue(initialInt, bits, inc); 544 545 if (!Double.isFinite(initial)) 546 initial = 0; 547 548 double fac = Math.exp(Math.abs(inc) * 15) / 1000; 549 double delta = Math.abs(initial == 0 ? 1 : initial) * fac * Math.signum(inc); 550 double exp = Math.pow(10, Math.floor(Math.log10(Math.abs(delta)))); 551 double val = Math.round((initial + delta) / exp) * exp; 552 553 if (bits == 32) 554 return Float.floatToIntBits((float) val); 555 else 556 return Double.doubleToLongBits(val); 557 } 558 559 @Override isSeparatorInFrontOf(int bits, int bit)560 public boolean isSeparatorInFrontOf(int bits, int bit) { 561 switch (bits) { 562 case 32: 563 return bit == 31 || bit == 23; 564 case 64: 565 return bit == 63 || bit == 52; 566 default: 567 return HEX_FORMATTER.isSeparatorInFrontOf(bits, bit); 568 } 569 } 570 } 571 572 } 573