Advertisement
iskunk

Attempt at CM-5 pattern emulation (rev. 6)

Apr 7th, 2016
199
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 6.79 KB | None | 0 0
  1. /* http://www.housedillon.com/?p=1272
  2.  * Written by iskunk (Daniel Richard G.) 2016 April
  3.  * printf("<%s@%s.%s>\n", "skunk", "iskunk", "org");
  4.  * THIS FILE IS IN THE PUBLIC DOMAIN
  5.  *
  6.  * Program to emulate a subset of the Thinking Machines Corp. CM-5 front
  7.  * LED panel display modes (revision 6)
  8.  */
  9.  
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <stdint.h>
  13. #include <string.h>
  14. #include <unistd.h>
  15. #include <assert.h>
  16.  
  17. #define NUM_ROWS 32     /* unique rows */
  18. #define NUM_ROWS_HALF 16    /* NUM_ROWS divided by 2 */
  19. #define NUM_ROWS_DISPLAYED 106  /* total rows in front panel display */
  20.  
  21. #define RNUM_SEED 0xBAD  /* :-) */
  22.  
  23. static uint16_t rnum = RNUM_SEED;
  24. static uint16_t rnum_8741 = RNUM_SEED;
  25.  
  26. /* A Galois LFSR requires a different seed to produce the same output
  27.  */
  28. static uint16_t rnum_galois = 0x917D;
  29.  
  30. /* Note: rows[0] is the top row; most significant bit is at left;
  31.  * a zero bit corresponds to a lit LED
  32.  */
  33. static uint16_t rows[NUM_ROWS];
  34.  
  35. /* Uninitialized bits, displayed briefly at the start of mode 7
  36.  */
  37. static const uint16_t rows_glitch[NUM_ROWS] = {
  38.     0x8F10, 0x9112, 0x9314, 0x9516, 0x18E9, 0x5899, 0x38D9, 0x78B9,
  39.     0x9F20, 0xA122, 0xA324, 0xA526, 0x14E5, 0x5495, 0x34D5, 0x74B5,
  40.     0xAF30, 0xB132, 0xB334, 0xB536, 0x1CED, 0x5C9D, 0x3CDD, 0x7CBD,
  41.     0xBF40, 0xC142, 0xC344, 0xC546, 0x12E3, 0x5293, 0x32D3, 0x72B3
  42. };
  43.  
  44. /* This is a rough translation of Jim's Intel 8741 assembler code into C.
  45.  * His original code and (lightly edited) comments are retained below.
  46.  */
  47. static uint16_t get_random_bit_8741(void)
  48. {
  49.     uint8_t RNUM   = rnum_8741 & 0xFF;  /* low-order byte */
  50.     uint8_t RNUMp1 = rnum_8741 >> 8;    /* high-order byte */
  51.     uint8_t A, C, tmp;
  52.  
  53. #define b(var, n) ((var >> n) & 1)  /* gets Nth bit */
  54. #define cpl(flag) flag = flag ^ 1
  55. #define jnb(val, label) if (!val) goto label
  56. #define orl(flag, val) flag |= val
  57. #define rrc(reg) tmp = reg & 1; reg = (C << 7) | (reg >> 1); C = tmp
  58.  
  59.     /* ;This subroutine implements a 16 bit random number generator based
  60.      * ;on the primitive polynomial:
  61.      * ;
  62.      * ;    1 + X + X^3 + X^12 + X^16
  63.      * ;
  64.      * ;The value is stored in memory as RNUM.  This subroutine returns
  65.      * ;with the low order byte of the RNUM in the accumulator and a random
  66.      * ;bit value in the Carry (C) bit.
  67.      * ;
  68.      */
  69.     C = b(RNUM,0);      /* mov C, RNUM.0    ;get the units value of the PP      */
  70.     jnb(b(RNUM,1), rand1);  /* jnb RNUM.1, rand1    ;jmp if X = 0               */
  71.     cpl(C);         /* cpl C        ;else compliment C (xor)        */
  72. rand1:  jnb(b(RNUM,3), rand2);  /* jnb RNUM.3, rand2    ;jmp if X^3 = 0             */
  73.     cpl(C);         /* cpl C        ;else compliment C (xor)        */
  74. rand2:  jnb(b(RNUMp1,4), rand3);  /* jnb (RNUM+1).4, rand3  ;jmp if X^12 = 0            */
  75.     cpl(C);         /* cpl C        ;else compliment C (xor)        */
  76. rand3:  A = RNUMp1;     /* mov A, RNUM+1    ;get high byte of RNUM          */
  77.     rrc(A);         /* rrc A        ;and rotate down (thru accumulator) */
  78.     RNUMp1 = A;     /* mov RNUM+1, A    ;save it back to memory         */
  79.     A = RNUM;       /* mov A, RNUM      ;get low byte of RNUM           */
  80.     rrc(A);         /* rrc A        ;and rotate down (thru accumulator) */
  81.     RNUM = A;       /* mov RNUM, A      ;save it back to memory         */
  82.     orl(C, b(RNUM,1));  /* orl C, RNUM.1    ;set C 75 percent of the time       */
  83.                 /* ret          ;return                 */
  84.  
  85. #undef b
  86. #undef cpl
  87. #undef jnb
  88. #undef orl
  89. #undef rrc
  90.  
  91.     rnum_8741 = RNUM | (RNUMp1 << 8);
  92.  
  93.     return C;
  94. }
  95.  
  96. /* "In a software implementation of an LFSR, the Galois form is more
  97.  * efficient as the XOR operations can be implemented a word at a
  98.  * time: only the output bit must be examined individually."
  99.  * -- https://en.wikipedia.org/wiki/Linear_feedback_shift_register
  100.  */
  101. static uint16_t get_random_bit_galois(void)
  102. {
  103. #define X rnum_galois
  104.  
  105.     uint16_t out_bit = X & 1;
  106.     uint16_t rand_bit = (X | (X >> 2)) & 1;
  107.  
  108.     X >>= 1;
  109.     X ^= (-out_bit) & 0xD008;
  110.  
  111. #undef X
  112.  
  113.     return rand_bit;
  114. }
  115.  
  116. static uint16_t get_random_bit(void)
  117. {
  118. #define X rnum
  119.  
  120.     /* https://en.wikipedia.org/wiki/Linear_feedback_shift_register
  121.      * Primitive polynomial: x^16 + x^15 + x^13 + x^4 + 1
  122.      */
  123.     uint16_t lfsr_bit = ((X >> 0) ^ (X >> 1) ^ (X >> 3) ^ (X >> 12)) & 1;
  124.  
  125.     uint16_t rand_bit = (X | (X >> 2)) & 1;
  126.  
  127.     X = (lfsr_bit << 15) | (X >> 1);
  128.  
  129. #undef X
  130.  
  131. #ifndef NDEBUG
  132.     /* Compare two alternative implementations of the pseudo-random bit
  133.      * generator function */
  134.     {
  135.     uint16_t rand_bit_8741   = get_random_bit_8741();
  136.     uint16_t rand_bit_galois = get_random_bit_galois();
  137.  
  138.     assert(rand_bit == rand_bit_8741);
  139.     assert(rand_bit_galois == rand_bit);
  140.  
  141.     assert(rnum == rnum_8741);
  142.     }
  143. #endif
  144.  
  145.     return rand_bit;
  146. }
  147.  
  148. static void print_row(uint16_t x)
  149. {
  150.     uint16_t m;
  151.     int pos;
  152.     char v[17];
  153.  
  154.     /* MSB at left, LSB at right
  155.      * 0 -> LED on, 1 -> LED off
  156.      */
  157.     for (m = 1 << 15, pos = 0; m != 0; m >>= 1, pos++)
  158.         v[pos] = (x & m) ? '-' : 'O';
  159.  
  160.     v[16] = '\0';
  161.  
  162.     puts(v);
  163. }
  164.  
  165. static void print_panel(void)
  166. {
  167.     int i;
  168.  
  169.     /* ANSI escape sequence to clear screen
  170.      */
  171.     fputs("\033[2J\033[1;1H", stdout);
  172.  
  173.     for (i = 0; i < NUM_ROWS_DISPLAYED; i++)
  174.         print_row(rows[i & 31]);
  175. }
  176.  
  177. int main(int argc, char **argv)
  178. {
  179.     int mode = 7;
  180.     int i, j;
  181.     int toggle = 0;
  182.  
  183.     if (argc == 2)
  184.     {
  185.         mode = (int)strtol(argv[1], NULL, 16);
  186.  
  187.         switch (mode)
  188.         {
  189.             case 5:
  190.             case 7:
  191.             case 9:
  192.             case 0xA:
  193.             case 0xB:
  194.             break;
  195.            
  196.             default:
  197.             printf("error: invalid mode \"%s\"\n", argv[1]);
  198.             printf("usage: %s [MODE]\n", argv[0]);
  199.             puts("valid options for MODE: 5 7 9 A B");
  200.             return 1;
  201.         }
  202.     }
  203.  
  204.     /* Initial state: all but 3 LEDs lit
  205.      */
  206.     memset(rows, 0, sizeof(rows));
  207.     rows[0] = 0x9400;
  208.     print_panel();
  209.     fflush(stdout);
  210.     usleep(600000); /* 600 ms */
  211.  
  212.     /* Initialize rows with glitch pattern
  213.      */
  214.     memcpy(rows, rows_glitch, sizeof(rows));
  215.  
  216.     for (;;)
  217.     {
  218.         switch (mode)
  219.         {
  220.             /* "random and pleasing" */
  221.             case 5:
  222.             for (i = 0; i < NUM_ROWS_HALF; i++)
  223.             {
  224.                 for (j = 0; j < 16; j++)
  225.                 {
  226.                     /* Note that the upper half of the
  227.                      * panel is one JTAG chain, and the
  228.                      * lower half is another */
  229.  
  230.                     uint16_t bit_lower = get_random_bit();
  231.                     uint16_t bit_upper = get_random_bit();
  232.  
  233.                     rows[i] <<= 1;
  234.                     rows[i] |= bit_upper;
  235.  
  236.                     rows[i + NUM_ROWS_HALF] <<= 1;
  237.                     rows[i + NUM_ROWS_HALF] |= bit_lower;
  238.                 }
  239.             }
  240.             break;
  241.  
  242.             /* "interleaved display of random and pleasing" */
  243.             case 7:
  244.             for (i = NUM_ROWS - 1; i >= 0; i--)
  245.             {
  246.                 uint16_t bit = get_random_bit();
  247.  
  248.                 if (i & 4)
  249.                     rows[i] = (bit << 15) | (rows[i] >> 1);
  250.                 else
  251.                     rows[i] = (rows[i] << 1) | bit;
  252.             }
  253.             break;
  254.  
  255.             /* "all leds on" */
  256.             case 9:
  257.             memset(rows, 0, sizeof(rows));
  258.             break;
  259.  
  260.             /* "all leds off" */
  261.             case 0xA:
  262.             memset(rows, 0xFF, sizeof(rows));
  263.             break;
  264.  
  265.             /* "continuous blink leds on then off" */
  266.             case 0xB:
  267.             memset(rows, toggle ? 0 : 0xFF, sizeof(rows));
  268.             toggle ^= 1;
  269.             break;
  270.         }
  271.  
  272.         print_panel();
  273.  
  274.         fflush(stdout);
  275.         usleep(200000); /* 200 ms */
  276.     }
  277.  
  278.     return 0;
  279. }
  280.  
  281. /* EOF */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement