Created
February 7, 2020 09:14
-
-
Save zeroeth/c0b83e7a856d01c9db46ee6d414a1654 to your computer and use it in GitHub Desktop.
cm-5 mode5 revision 5 (newer one in older gists)
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.) | |
* THIS FILE IS IN THE PUBLIC DOMAIN | |
* | |
* Program to emulate the CM-5's "random and pleasing" LED panel mode | |
* (revision 5, "House Dillon video exclusive" edition) | |
*/ | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <assert.h> | |
#define NUM_ROWS 32 /* unique rows */ | |
#define NUM_ROWS_DISPLAYED 106 /* rows in front panel display */ | |
/* Dillon's video shows a single 16x32 panel */ | |
#undef NUM_ROWS_DISPLAYED | |
#define NUM_ROWS_DISPLAYED 32 | |
#ifndef NDEBUG | |
# define COMPARE_8741 | |
#endif | |
#define RNUM_SEED 0xBAD | |
static uint16_t rnum = RNUM_SEED; | |
static uint16_t rnum_8741 = RNUM_SEED; | |
/* 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]; | |
/* The source of this pattern is not [yet] known. Note that all these rows | |
* will be shifted by one bit before being displayed. | |
*/ | |
static const uint16_t rows_mystery[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 | |
}; | |
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; | |
#undef X | |
rnum = (lfsr_bit << 15) | (rnum >> 1); | |
return rand_bit; | |
} | |
/* This is a rough translation of Jim's Intel 8741 assembler code into C | |
*/ | |
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 ;light LED only 25 percent of the time */ | |
/* ret ;return */ | |
#undef b | |
#undef cpl | |
#undef jnb | |
#undef orl | |
#undef rrc | |
rnum_8741 = RNUM | (RNUMp1 << 8); | |
return C; | |
} | |
static void print_row(uint16_t x) | |
{ | |
uint16_t m; | |
int pos; | |
char v[17]; | |
/* 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 sequence to clear terminal */ | |
fputs("\033[2J\033[1;1H", stdout); | |
for (i = 0; i < NUM_ROWS_DISPLAYED; i++) | |
print_row(rows[i & 31]); | |
} | |
int main(void) | |
{ | |
int i; | |
int t = 2; | |
/* 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 mystery pattern */ | |
memcpy(rows, rows_mystery, sizeof(rows)); | |
for (;;) | |
{ | |
for (i = NUM_ROWS - 1; i >= 0; i--) | |
{ | |
uint16_t bit = get_random_bit(); | |
#ifdef COMPARE_8741 | |
uint16_t bit_8741 = get_random_bit_8741(); | |
assert(bit == bit_8741); | |
assert(rnum == rnum_8741); | |
#endif | |
if (i & 4) | |
rows[i] = (bit << 15) | (rows[i] >> 1); | |
else | |
rows[i] = (rows[i] << 1) | bit; | |
} | |
print_panel(); | |
/* Print timestamp for matching to Dillon's video */ | |
if (++t < 300) | |
printf("\n00:00:%02d:%02d\n", t / 5, 6 * (t % 5)); | |
fflush(stdout); | |
usleep(200000); /* 200 ms */ | |
} | |
return 0; | |
} | |
/* EOF */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment