-- ColorKinetic.mesa
-- Last edited by Doug Wyatt, 17-Dec-81 14:00:19
-- Pilot version
DIRECTORY
BitBlt,
ColorDisplay,
Inline,
Keys USING [KeyBits],
Process USING [MsecToTicks, Pause],
UserTerminal USING[keyboard];
ColorKinetic: PROGRAM
IMPORTS BitBlt, ColorDisplay, Inline, Process, UserTerminal =
BEGIN
keys: LONG POINTER TO Keys.KeyBits ← LOOPHOLE[UserTerminal.keyboard];
sym: BOOLEAN ← FALSE;
bbspace: BitBlt.BBTableSpace;
bb: BitBlt.BBptr ← InitBB[@bbspace];
grayword: CARDINAL ← 0;
bitmap,bitmapB: LONG POINTER ← NIL;
wpl,wplB: CARDINAL ← 0;
width,height: CARDINAL ← 0;
lbpp,lbppB: CARDINAL ← 0; -- log (base 2) bits per pixel
bpp,bppB: CARDINAL ← 0; -- bits per pixel
full: BOOLEAN ← FALSE;
ashow,bshow: BOOLEAN ← TRUE;
splat: BOOLEAN ← TRUE;
test: BOOLEAN ← FALSE;
neg: BOOLEAN ← FALSE;
cornerSize: CARDINAL ← 4;
cornerColor: CARDINAL ← 8;
Triple: TYPE = RECORD[r,g,b: [0..256)];
testmap: ARRAY[0..8] OF Triple ← [
[127,127,127], -- gray (background)
[ 0, 0, 0], -- black
[255, 0, 0], -- red
[ 0,255, 0], -- green
[255,255, 0], -- yellow
[ 0, 0,255], -- blue
[255, 0,255], -- magenta
[ 0,255,255], -- cyan
[255,255,255] -- white
];
TestPattern: PROC = {
x: CARDINAL = 60;
h: CARDINAL = 60;
w: CARDINAL = 50;
gap: CARDINAL = 15;
IF NOT full THEN {
FOR i: CARDINAL IN[0..9) DO
ColorDisplay.SetColor[pixelA: i, r: testmap[i].r, g: testmap[i].g, b: testmap[i].b];
ENDLOOP;
ColorDisplay.Show[TRUE,FALSE,FALSE];
};
-- gray background
Rect[0,0,width,height,0];
-- four corners
{ s: CARDINAL = cornerSize;
c: CARDINAL = cornerColor; -- color
Rect[0,0,s,s,c];
Rect[width-s,0,s,s,c];
Rect[0,height-s,s,s,c];
Rect[width-s,height-s,s,s,c];
};
-- various colors at left edge
FOR i: CARDINAL IN[0..8) DO
r,g,b: [0..256);
r ← (i MOD 2)*255;
g ← ((i/2) MOD 2)*255;
b ← ((i/4) MOD 2)*255;
Rect[4,i*h+10,x,h-20,i+1];
ENDLOOP;
-- a bunch of stripes
FOR i: CARDINAL IN[0..8) DO
r,g,b: [0..256);
r ← (i MOD 2)*255;
g ← ((i/2) MOD 2)*255;
b ← ((i/4) MOD 2)*255;
Rect[x+i*w+gap,0,w-gap,8*h,i+1];
ENDLOOP;
};
Rect: PROC[x,y,w,h: CARDINAL, i: [0..8]] = {
IF NOT (x<width AND y<height) THEN RETURN;
w ← MIN[w,width-x]; h ← MIN[h,height-y];
IF full THEN FullRect[x,y,w,h,testmap[i].r,testmap[i].g,testmap[i].b]
ELSE ARect[x,y,w,h,i];
};
SetUpColors: PROCEDURE = BEGIN
k: CARDINAL ← Inline.BITSHIFT[1,bpp]; -- number of pixel values
FOR n: CARDINAL IN [0..k) DO
r,g,b: [0..256);
Alter: PROC[c: [0..256)] RETURNS[[0..256)] = INLINE { RETURN[255-(255-c)/2] };
r ← MyRandom[256]; g ← MyRandom[256]; b ← MyRandom[256];
ColorDisplay.SetColor[pixelA: n, pixelB: 0, r: r, g: g, b: b];
ColorDisplay.SetColor[pixelA: n, pixelB: 1, r: Alter[r], g: Alter[g], b: Alter[b]];
ENDLOOP;
END;
Positive: PROC = {
IF NOT full THEN RETURN;
ColorDisplay.TurnOff[];
FOR i: CARDINAL IN[0..256) DO
ColorDisplay.SetRedMap[i,i];
ColorDisplay.SetGreenMap[i,i];
ColorDisplay.SetBlueMap[i,i];
ENDLOOP;
ColorDisplay.TurnOn[];
};
Negative: PROC = {
IF NOT full THEN RETURN;
ColorDisplay.TurnOff[];
FOR i: CARDINAL IN[0..256) DO
ColorDisplay.SetRedMap[i,255-i];
ColorDisplay.SetGreenMap[i,255-i];
ColorDisplay.SetBlueMap[i,255-i];
ENDLOOP;
ColorDisplay.TurnOn[];
};
MyRandom: PROCEDURE [max: CARDINAL] RETURNS [CARDINAL] = INLINE BEGIN
RETURN[Random[] MOD max];
END;
SetGray: PROC[g: [0..256)] = { grayword ← MakeGray[g] };
MakeGray: PROC[g: [0..256)] RETURNS[CARDINAL] = {
grayword: CARDINAL ← 0;
IF bpp#0 THEN {
ppw: CARDINAL ← 16/bpp; -- pixels per word
mask: CARDINAL ← Inline.BITNOT[Inline.BITSHIFT[-1,bpp]];
g ← Inline.BITAND[g,mask];
THROUGH [0..ppw) DO grayword ← Inline.BITSHIFT[grayword,bpp] + g ENDLOOP;
};
RETURN[grayword];
};
InitBB: PROC[bbs: POINTER TO BitBlt.BBTableSpace] RETURNS[BitBlt.BBptr] = INLINE {
bb: BitBlt.BBptr ← BitBlt.AlignedBBTable[bbs];
bb↑ ← [
dst: [word: NIL, bit: 0],
dstBpl: 0,
src: [word: @grayword, bit: 0],
srcDesc: [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]]],
width: 0, height: 0,
flags: [disjoint: TRUE, gray: TRUE]
];
RETURN[bb];
};
FullRect: PROC[x,y,w,h: CARDINAL, r,g,b: [0..256)] = {
rgword: CARDINAL ← 256*r + g;
bbword: CARDINAL ← 256*b + b;
IF NOT full THEN RETURN;
-- bitmap A
bb.dst ← [word: ColorDisplay.baseA + LONG[y]*ColorDisplay.wplA + x, bit: 0];
bb.dstBpl ← 16*ColorDisplay.wplA;
bb.src ← [word: @rgword, bit: 0];
bb.width ← 16*w;
bb.height ← h;
bb.flags.dstFunc ← null;
BitBlt.BITBLT[bb];
-- bitmap B
bb.dst ← [word: ColorDisplay.baseB + LONG[y]*ColorDisplay.wplB + x/2, bit: 8*(x MOD 2)];
bb.dstBpl ← 16*ColorDisplay.wplB;
bb.src ← [word: @bbword, bit: bb.dst.bit];
bb.width ← 8*w;
bb.height ← h;
bb.flags.dstFunc ← null;
BitBlt.BITBLT[bb];
};
ARect: PROC[x,y,w,h: CARDINAL, i: [0..256), fn: BitBlt.DstFunc ← null] = {
iword: CARDINAL ← MakeGray[i];
xbit: CARDINAL ← Inline.BITSHIFT[x,lbpp];
bb.dst ← [word: ColorDisplay.baseA + LONG[y]*ColorDisplay.wplA + xbit/16, bit: xbit MOD 16];
bb.dstBpl ← 16*ColorDisplay.wplA;
bb.src ← [word: @iword, bit: bb.dst.bit];
bb.width ← Inline.BITSHIFT[w,lbpp];
bb.height ← h;
bb.flags.dstFunc ← fn;
BitBlt.BITBLT[bb];
};
index: [0..256) ← 0;
Rectangle: PROC[lx,ty,rx,by: CARDINAL, fn: BitBlt.DstFunc ← null] = {
IF full THEN {
r,g,b: [0..256);
r ← MyRandom[256]; g ← MyRandom[256]; b ← MyRandom[256];
FullRect[lx,ty,rx-lx,by-ty,r,g,b];
}
ELSE {
index ← (index + 1) MOD 256;
ARect[lx,ty,rx-lx,by-ty,index,fn];
};
};
BRectangle: PROC[lx,ty,rx,by: CARDINAL] = {
xbit: CARDINAL ← Inline.BITSHIFT[lx,lbppB];
black: CARDINAL ← 177777B;
bb.dst ← [word: bitmapB + LONG[ty]*wplB + xbit/16, bit: xbit MOD 16];
bb.dstBpl ← 16*wplB;
bb.src ← [word: @black, bit: 0];
bb.width ← Inline.BITSHIFT[(rx-lx),lbppB];
bb.height ← by-ty;
bb.flags.dstFunc ← xor;
BitBlt.BITBLT[bb];
};
RandomSplat: PROCEDURE = BEGIN
screenX: CARDINAL = width;
screenY: CARDINAL = height;
XorRatio: CARDINAL = 3;
ty, ty2, lx, w, h: CARDINAL;
fn: BitBlt.DstFunc ← (IF MyRandom[XorRatio]=0 THEN xor ELSE null);
SetGray[MyRandom[256]];
IF sym THEN BEGIN
ty ← ty2 ← MyRandom[screenY/2];
lx ← MyRandom[screenX/2];
w ← MyRandom[screenX/2-lx];
h ← MyRandom[screenY/2-ty];
Rectangle[lx, ty, lx+w, ty+h, fn];
ty ← screenY/2+(screenY/2-ty)-h;
Rectangle[lx, ty, lx+w, ty+h, fn];
lx ← screenX/2+(screenX/2-lx)-w;
Rectangle[lx, ty, lx+w, ty+h, fn];
Rectangle[lx, ty2, lx+w, ty2+h, fn];
END
ELSE BEGIN
ty ← MyRandom[screenY];
lx ← MyRandom[screenX];
w ← MyRandom[screenX-lx];
h ← MyRandom[screenY-ty];
Rectangle[lx, ty, lx+w, ty+h, fn];
END;
IF bitmapB#NIL THEN {
ty ← MyRandom[screenY];
lx ← MyRandom[screenX];
w ← MyRandom[screenX-lx];
h ← MyRandom[screenY-ty];
BRectangle[lx, ty, lx+w, ty+h];
};
IF keys[Lock]=down THEN BEGIN
Wait[200]; -- slow mode
IF keys[S]=down THEN BEGIN
sym ← ~sym;
ClearScreen[];
WHILE keys[S]=down DO ENDLOOP;
END;
WHILE keys[F]=down DO
IF full THEN LOOP;
IF keys[C]=down THEN {
SetUpColors; Wait[500];
UNTIL keys[C]=up DO SetUpColors; Wait[200] ENDLOOP;
};
IF keys[R]=down THEN {
Roll; Wait[500];
UNTIL keys[R]=up DO Roll; Wait[100] ENDLOOP;
};
ENDLOOP;
END;
END;
Roll: PROC = {
r,g,b,r0,g0,b0: [0..256);
[r0,g0,b0] ← ColorDisplay.GetColor[0];
FOR i: CARDINAL IN[0..255) DO
[r,g,b] ← ColorDisplay.GetColor[i+1];
ColorDisplay.SetColor[i,0,r,g,b];
ENDLOOP;
ColorDisplay.SetColor[255,0,r0,g0,b0];
};
SetBackground: PROCEDURE [v: INTEGER] = BEGIN
background ← v;
ColorDisplay.SetColor[0, 0, v, v, v];
END;
background: INTEGER ← 255; -- initially white
ClearScreen: PROCEDURE = {
IF NOT full THEN {
ColorDisplay.SetColor[0, 0, background, background, background];
SetGray[0] };
Rectangle[0,0,width,height];
};
Wait: PROCEDURE[millisecs: CARDINAL] = INLINE {
Process.Pause[Process.MsecToTicks[millisecs]] };
-- Constant definitions (meanings described in InitRandom below)
defaultSeed: CARDINAL = 27183;
numCalls: INTEGER = 3;
-- Module state
a: ARRAY [0..55] OF CARDINAL;
-- Holds 55 random cardinals, to be returned by Random. (A[0] is wasted to make the
--code generator produce better array accesses.)
p: INTEGER [0..55];
-- a[1..p-1] has not yet been returned by Random; p is [1..55] except within Random.
-- Procedures
InitRandom: PUBLIC PROC[seed: INTEGER] RETURNS[INTEGER] = {
-- The parameter seed determines the sequence generated by procedure Random below.
-- If seed=0, a default seed value is used to determine the starting point of the
--sequence; if seed>0, seed is scaled if necessary and then used; if seed<0, a seed
--value is derived from the system clock. In any case, the seed value actually used
--(after scaling) is the integer value returned.
minSeed: CARDINAL = LAST[CARDINAL]/10;
g, gPrev, gSave: CARDINAL;
IF seed<=0 THEN seed ← defaultSeed;
-- Now scale the seed into the proper range (no log routine available...)
WHILE seed<minSeed DO seed ← seed*3 ENDLOOP;
-- Seed can't be too big since LAST[INTEGER] < LAST[CARDINAL]*(9/10)
-- The array a is initialized by placing seed in a[55], and scattering the values
-- (-1)**(i-1) * (F(i) - seed*F(i-1)) MOD maxRand, 0<i<55,
-- (where F(i) denotes the i-th Fibonacci number) throughout the rest of a. Then
--the generating procedure RandomGen is called, numCalls times, to make things
--sufficiently random.
a[55] ← gPrev ← seed;
g ← 1;
FOR i: INTEGER IN [1..54] DO
p ← (21*i) MOD 55;
a[p] ← gSave ← g; g ← gPrev-g; gPrev ← gSave;
ENDLOOP;
THROUGH [1..numCalls) DO
RandomGen[];
ENDLOOP;
-- Show a as being empty; first call to Random will call RandomGen again.
p ← 1;
RETURN[seed]
};--InitRandom
Random: PUBLIC PROC RETURNS[CARDINAL] = INLINE {
p ← p-1;
IF p=0 THEN { RandomGen[]; p ← 55 };
RETURN[a[p]]
};--Random
RandomGen: PROC = INLINE {
-- Additive random number generator using the recurrence y(n) = y(n-55) - y(n-24)
--mod maxRand. See John F. Reiser, "Analysis of Additive Random Number Generators",
--STAN-CS-77-601, March 1977, or Knuth Volume 2 (second edition.)
FOR i: INTEGER IN [1..24] DO
a[i] ← a[i] - a[i+31];
ENDLOOP;
FOR i: INTEGER IN [25..55] DO
a[i] ← a[i] - a[i-24];
ENDLOOP;
};--RandomGen
Choose: PUBLIC PROC[min, max: CARDINAL] RETURNS[CARDINAL--[min..max]--] = {
intervalLen: CARDINAL;
IF min > max THEN ERROR;
intervalLen ← max - min + 1; --is 0 when min=0, max=LAST[CARDINAL]
IF intervalLen = 0 THEN RETURN[Random[]];
DO
-- Draw a number in [0..LAST[CARDINAL]]. We want to reject this number if it lies in the
--"odd interval" at the high end of this range (there is no odd interval if intervalLen
--divides 2↑16). The funny test below does it (claim). The average number of numbers drawn
--is less than 2, and much closer to 1 for small intervalLen.
r, rem: CARDINAL;
-- Inline expansion of Random[];
p ← p-1;
IF p=0 THEN { RandomGen[]; p ← 55 };
r ← a[p];
rem ← r MOD intervalLen;
IF (r - rem) > LOOPHOLE[-LOOPHOLE[intervalLen,INTEGER],CARDINAL] THEN LOOP;
RETURN[min + rem];
ENDLOOP;
};--Choose
SetLogBitsPerPixel: PROC[n: [0..4)] RETURNS[BOOLEAN] = {
b: CARDINAL ← Inline.BITSHIFT[1,n];
mode: ColorDisplay.Mode ← [FALSE,b,1];
IF NOT ColorDisplay.HasMode[mode] THEN {
mode.bitsPerPixelB ← 0;
IF NOT ColorDisplay.HasMode[mode] THEN RETURN[FALSE];
};
IF NOT ColorDisplay.SetMode[mode] THEN ERROR;
lbpp ← n; bpp ← b; full ← FALSE;
lbppB ← 0; bppB ← 1;
ashow ← bshow ← TRUE;
width ← ColorDisplay.width; height ← ColorDisplay.height;
bitmap ← ColorDisplay.baseA; wpl ← ColorDisplay.wplA;
bitmapB ← ColorDisplay.baseB; wplB ← ColorDisplay.wplB;
ClearScreen;
SetUpColors;
ColorDisplay.TurnOn[];
RETURN[TRUE];
};
Set24BitsPerPixel: PROC = {
mode: ColorDisplay.Mode ← [TRUE,0,0];
IF NOT ColorDisplay.HasMode[mode] THEN RETURN;
IF NOT ColorDisplay.SetMode[mode] THEN ERROR;
lbpp ← 0; bpp ← 0; full ← TRUE;
bitmap ← NIL; bitmapB ← NIL; wpl ← 0;
ashow ← bshow ← TRUE;
width ← ColorDisplay.width; height ← ColorDisplay.height;
IF test THEN { TestPattern[]; test ← FALSE } ELSE ClearScreen;
IF neg THEN Negative ELSE Positive;
ColorDisplay.TurnOn[];
};
[] ← InitRandom[0];
IF NOT SetLogBitsPerPixel[3] THEN [] ← SetLogBitsPerPixel[2];
-- PrintDirections;
DO
IF keys[E]=down THEN EXIT;
IF keys[One]=down AND bpp#1 THEN { [] ← SetLogBitsPerPixel[0];
IF NOT splat THEN test ← TRUE };
IF keys[Two]=down AND bpp#2 THEN { [] ← SetLogBitsPerPixel[1];
IF NOT splat THEN test ← TRUE };
IF keys[Four]=down AND bpp#4 THEN { [] ← SetLogBitsPerPixel[2];
IF NOT splat THEN test ← TRUE };
IF keys[Eight]=down AND bpp#8 THEN { [] ← SetLogBitsPerPixel[3];
IF NOT splat THEN test ← TRUE };
IF keys[Zero]=down AND NOT full THEN { Set24BitsPerPixel[];
IF NOT splat THEN test ← TRUE };
IF keys[A]=down THEN { ashow ← NOT ashow; ColorDisplay.Show[ashow,bshow,TRUE];
WHILE keys[A]=down DO ENDLOOP };
IF keys[B]=down THEN { bshow ← NOT bshow; ColorDisplay.Show[ashow,bshow,TRUE];
WHILE keys[B]=down DO ENDLOOP };
IF keys[R]=down THEN { splat ← NOT splat;
WHILE keys[R]=down DO ENDLOOP };
IF keys[N]=down THEN { neg ← NOT neg;
WHILE keys[N]=down DO ENDLOOP };
IF keys[T]=down THEN { test ← TRUE; splat ← FALSE };
IF test THEN { TestPattern[]; test ← FALSE };
IF splat THEN RandomSplat;
ENDLOOP;
IF NOT ColorDisplay.SetMode[ColorDisplay.disconnected] THEN ERROR;
END.