Created
January 17, 2017 06:11
-
-
Save zeroeth/1f1b1d4cbc1841b48290ff3aa0ad6869 to your computer and use it in GitHub Desktop.
CM-5 LED code mode 5
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* http://www.housedillon.com/?p=1272 | |
* Written by iskunk (Daniel Richard G.) 2016 April | |
* printf("<%s@%s.%s>\n", "skunk", "iskunk", "org"); | |
* THIS FILE IS IN THE PUBLIC DOMAIN | |
* | |
* Program to emulate a subset of the Thinking Machines Corp. CM-5 front | |
* LED panel display modes (revision 6) | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <assert.h> | |
#define NUM_ROWS 32 /* unique rows */ | |
#define NUM_ROWS_HALF 16 /* NUM_ROWS divided by 2 */ | |
#define NUM_ROWS_DISPLAYED 106 /* total rows in front panel display */ | |
#define RNUM_SEED 0xBAD /* :-) */ | |
static uint16_t rnum = RNUM_SEED; | |
static uint16_t rnum_8741 = RNUM_SEED; | |
/* A Galois LFSR requires a different seed to produce the same output | |
*/ | |
static uint16_t rnum_galois = 0x917D; | |
/* Note: rows[0] is the top row; most significant bit is at left; | |
* a zero bit corresponds to a lit LED | |
*/ | |
static uint16_t rows[NUM_ROWS]; | |
/* Uninitialized bits, displayed briefly at the start of mode 7 | |
*/ | |
static const uint16_t rows_glitch[NUM_ROWS] = { | |
0x8F10, 0x9112, 0x9314, 0x9516, 0x18E9, 0x5899, 0x38D9, 0x78B9, | |
0x9F20, 0xA122, 0xA324, 0xA526, 0x14E5, 0x5495, 0x34D5, 0x74B5, | |
0xAF30, 0xB132, 0xB334, 0xB536, 0x1CED, 0x5C9D, 0x3CDD, 0x7CBD, | |
0xBF40, 0xC142, 0xC344, 0xC546, 0x12E3, 0x5293, 0x32D3, 0x72B3 | |
}; | |
/* This is a rough translation of Jim's Intel 8741 assembler code into C. | |
* His original code and (lightly edited) comments are retained below. | |
*/ | |
static uint16_t get_random_bit_8741(void) | |
{ | |
uint8_t RNUM = rnum_8741 & 0xFF; /* low-order byte */ | |
uint8_t RNUMp1 = rnum_8741 >> 8; /* high-order byte */ | |
uint8_t A, C, tmp; | |
#define b(var, n) ((var >> n) & 1) /* gets Nth bit */ | |
#define cpl(flag) flag = flag ^ 1 | |
#define jnb(val, label) if (!val) goto label | |
#define orl(flag, val) flag |= val | |
#define rrc(reg) tmp = reg & 1; reg = (C << 7) | (reg >> 1); C = tmp | |
/* ;This subroutine implements a 16 bit random number generator based | |
* ;on the primitive polynomial: | |
* ; | |
* ; 1 + X + X^3 + X^12 + X^16 | |
* ; | |
* ;The value is stored in memory as RNUM. This subroutine returns | |
* ;with the low order byte of the RNUM in the accumulator and a random | |
* ;bit value in the Carry (C) bit. | |
* ; | |
*/ | |
C = b(RNUM,0); /* mov C, RNUM.0 ;get the units value of the PP */ | |
jnb(b(RNUM,1), rand1); /* jnb RNUM.1, rand1 ;jmp if X = 0 */ | |
cpl(C); /* cpl C ;else compliment C (xor) */ | |
rand1: jnb(b(RNUM,3), rand2); /* jnb RNUM.3, rand2 ;jmp if X^3 = 0 */ | |
cpl(C); /* cpl C ;else compliment C (xor) */ | |
rand2: jnb(b(RNUMp1,4), rand3); /* jnb (RNUM+1).4, rand3 ;jmp if X^12 = 0 */ | |
cpl(C); /* cpl C ;else compliment C (xor) */ | |
rand3: A = RNUMp1; /* mov A, RNUM+1 ;get high byte of RNUM */ | |
rrc(A); /* rrc A ;and rotate down (thru accumulator) */ | |
RNUMp1 = A; /* mov RNUM+1, A ;save it back to memory */ | |
A = RNUM; /* mov A, RNUM ;get low byte of RNUM */ | |
rrc(A); /* rrc A ;and rotate down (thru accumulator) */ | |
RNUM = A; /* mov RNUM, A ;save it back to memory */ | |
orl(C, b(RNUM,1)); /* orl C, RNUM.1 ;set C 75 percent of the time */ | |
/* ret ;return */ | |
#undef b | |
#undef cpl | |
#undef jnb | |
#undef orl | |
#undef rrc | |
rnum_8741 = RNUM | (RNUMp1 << 8); | |
return C; | |
} | |
/* "In a software implementation of an LFSR, the Galois form is more | |
* efficient as the XOR operations can be implemented a word at a | |
* time: only the output bit must be examined individually." | |
* -- https://en.wikipedia.org/wiki/Linear_feedback_shift_register | |
*/ | |
static uint16_t get_random_bit_galois(void) | |
{ | |
#define X rnum_galois | |
uint16_t out_bit = X & 1; | |
uint16_t rand_bit = (X | (X >> 2)) & 1; | |
X >>= 1; | |
X ^= (-out_bit) & 0xD008; | |
#undef X | |
return rand_bit; | |
} | |
static uint16_t get_random_bit(void) | |
{ | |
#define X rnum | |
/* https://en.wikipedia.org/wiki/Linear_feedback_shift_register | |
* Primitive polynomial: x^16 + x^15 + x^13 + x^4 + 1 | |
*/ | |
uint16_t lfsr_bit = ((X >> 0) ^ (X >> 1) ^ (X >> 3) ^ (X >> 12)) & 1; | |
uint16_t rand_bit = (X | (X >> 2)) & 1; | |
X = (lfsr_bit << 15) | (X >> 1); | |
#undef X | |
#ifndef NDEBUG | |
/* Compare two alternative implementations of the pseudo-random bit | |
* generator function */ | |
{ | |
uint16_t rand_bit_8741 = get_random_bit_8741(); | |
uint16_t rand_bit_galois = get_random_bit_galois(); | |
assert(rand_bit == rand_bit_8741); | |
assert(rand_bit_galois == rand_bit); | |
assert(rnum == rnum_8741); | |
} | |
#endif | |
return rand_bit; | |
} | |
static void print_row(uint16_t x) | |
{ | |
uint16_t m; | |
int pos; | |
char v[17]; | |
/* MSB at left, LSB at right | |
* 0 -> LED on, 1 -> LED off | |
*/ | |
for (m = 1 << 15, pos = 0; m != 0; m >>= 1, pos++) | |
v[pos] = (x & m) ? '-' : 'O'; | |
v[16] = '\0'; | |
puts(v); | |
} | |
static void print_panel(void) | |
{ | |
int i; | |
/* ANSI escape sequence to clear screen | |
*/ | |
fputs("\033[2J\033[1;1H", stdout); | |
for (i = 0; i < NUM_ROWS_DISPLAYED; i++) | |
print_row(rows[i & 31]); | |
} | |
int main(int argc, char **argv) | |
{ | |
int mode = 7; | |
int i, j; | |
int toggle = 0; | |
if (argc == 2) | |
{ | |
mode = (int)strtol(argv[1], NULL, 16); | |
switch (mode) | |
{ | |
case 5: | |
case 7: | |
case 9: | |
case 0xA: | |
case 0xB: | |
break; | |
default: | |
printf("error: invalid mode \"%s\"\n", argv[1]); | |
printf("usage: %s [MODE]\n", argv[0]); | |
puts("valid options for MODE: 5 7 9 A B"); | |
return 1; | |
} | |
} | |
/* Initial state: all but 3 LEDs lit | |
*/ | |
memset(rows, 0, sizeof(rows)); | |
rows[0] = 0x9400; | |
print_panel(); | |
fflush(stdout); | |
usleep(600000); /* 600 ms */ | |
/* Initialize rows with glitch pattern | |
*/ | |
memcpy(rows, rows_glitch, sizeof(rows)); | |
for (;;) | |
{ | |
switch (mode) | |
{ | |
/* "random and pleasing" */ | |
case 5: | |
for (i = 0; i < NUM_ROWS_HALF; i++) | |
{ | |
for (j = 0; j < 16; j++) | |
{ | |
/* Note that the upper half of the | |
* panel is one JTAG chain, and the | |
* lower half is another */ | |
uint16_t bit_lower = get_random_bit(); | |
uint16_t bit_upper = get_random_bit(); | |
rows[i] <<= 1; | |
rows[i] |= bit_upper; | |
rows[i + NUM_ROWS_HALF] <<= 1; | |
rows[i + NUM_ROWS_HALF] |= bit_lower; | |
} | |
} | |
break; | |
/* "interleaved display of random and pleasing" */ | |
case 7: | |
for (i = NUM_ROWS - 1; i >= 0; i--) | |
{ | |
uint16_t bit = get_random_bit(); | |
if (i & 4) | |
rows[i] = (bit << 15) | (rows[i] >> 1); | |
else | |
rows[i] = (rows[i] << 1) | bit; | |
} | |
break; | |
/* "all leds on" */ | |
case 9: | |
memset(rows, 0, sizeof(rows)); | |
break; | |
/* "all leds off" */ | |
case 0xA: | |
memset(rows, 0xFF, sizeof(rows)); | |
break; | |
/* "continuous blink leds on then off" */ | |
case 0xB: | |
memset(rows, toggle ? 0 : 0xFF, sizeof(rows)); | |
toggle ^= 1; | |
break; | |
} | |
print_panel(); | |
fflush(stdout); | |
usleep(200000); /* 200 ms */ | |
} | |
return 0; | |
} | |
/* EOF */ | |
0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment