-- ColorKinetic.mesa -- Last edited by Doug Wyatt, 31-Mar-82 15:30:23 -- 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[]; }; Go: PROC = { [] ← InitRandom[0]; SELECT TRUE FROM SetLogBitsPerPixel[3] => NULL; SetLogBitsPerPixel[2] => NULL; ENDCASE => RETURN; -- 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; }; Go[]; END.