CDCMosB8BitColors.mesa
Copyright © 1984, 1987 by Xerox Corporation. All rights reserved.
Last edited by: Monier, September 24, 1984 9:55:48 pm PDT
Last edited by: Christian Jacobi, October 29, 1984 5:46:54 pm PST
Last edited by: Christian Jacobi, March 12, 1987 5:39:58 pm PST
DIRECTORY
Basics,
Buttons,
CD,
CDColors,
CDEnvironment,
CDSequencer,
CMosB,
Commander,
Containers,
Convert,
Imager,
ImagerColor,
ImagerColorMap,
ImagerDitherContext,
InterminalBackdoor,
Icons,
Labels,
PopUpSelection,
Process,
Real,
Rope,
Sliders,
Terminal,
TerminalIO,
UserProfile,
ViewerOps,
ViewerClasses;
CDCMosB8BitColors: CEDAR PROGRAM
IMPORTS Basics, Buttons, CDColors, CDEnvironment, CDSequencer, CMosB, Commander, Containers, Convert, Icons, ImagerColor, ImagerColorMap, --ImagerDitherContext,-- InterminalBackdoor, Labels, PopUpSelection, Process, Real, Rope, Sliders, Terminal, TerminalIO, UserProfile, ViewerOps =
BEGIN
terminal: Terminal.Virtual ← InterminalBackdoor.terminal;
--**color device**************************************
useMapEntries: ImagerDitherContext.MapEntries ← NIL;
ForceColors: PROC [mapEntries: ImagerDitherContext.MapEntries] = {
useMapEntries ← mapEntries;
ImagerColorMap.LoadEntries[vt: terminal, mapEntries: mapEntries, gamma: 1, shared: FALSE];
[] ← Terminal.SetColorCursorPresentation[terminal, onesAreWhite];
};
--**end color device**************************************
ColorRec: TYPE = RECORD[r, g, b, w: REAL ← 0, name: Rope.ROPENIL];
--w is a wight; The color of an intersection (i.e. green1 over blue) is the
--weighted sum of the rgb of each color.
ColorDescRep: TYPE = ARRAY [0..numberOfColors) OF ColorRec;
--index 0 is supposed to be background
Exception: TYPE = RECORD [x: CARDINAL, c: ColorRec, replace: BOOLTRUE];
--XXXXXXXXXXXXXXXX TECHNOLOGY XXXXXXXXXXXXXXXX
technology: CD.Technology ← CMosB.cmosB;
technologyName: Rope.ROPE ← technology.name;
numberOfColors: INT = 9; --background, normal colors and extra colors
numberOfNormalColors: INT = 7; --without background and extra colors
cDefaults: REF ColorDescRep ← NEW[ColorDescRep ← [
[0.49, 0.62, 0.62, 0.05, "Sub"], --background
[0, 1, 0, 1, "pD"],
[0, 1, 0, 1, "nD"],
[0.5, 0.5, 1, 0.5, "m1"],
[0.7, 0.4, 0.7, 0.4, "m2"],
[1, 0.2, 0, 1, "pol"],
[0.8, 0.8, 0.5, 0.15, "nW"],
[0.60, 0.70, 0.75, 0.15, "pW"],
[1, 0.95, 0.0, 0.9, "Xx"] --channel hack
]];
channelX: INT ← 8;
SetupSpecials: PROC [] =
BEGIN
cc: ColorRec ← currentColor[8]; --channel hack
x: INT ← channelX+1;
specials1 ← LIST[
[x: 1+16, c: [cc.r, cc.g, -cc.b, x*cc.w-1], replace: FALSE], --yellow on transistor
[x: 2+16, c: [cc.r, cc.g, -cc.b, x*cc.w-1], replace: FALSE], --yellow on transistor
[x: 32+64, c: [1, 1, 1, 1], replace: TRUE], --hack white error message
[x: 1+32, c: [1, 1, 1, 1], replace: TRUE], --hack white error message
[x: 2+64, c: [1, 1, 1, 1], replace: TRUE], --hack white error message
[x: 127, c: [0, 0, 0, 1], replace: TRUE] --black for cuts
];
specials2 ← LIST[
[x: 2, c: [1, 1, 1, 1], replace: TRUE], --pdiff without well
[x: 1+2, c: [0.8, 0.8, 0.8, 1], replace: TRUE] --light grey for inverted white
];
specials3 ← LIST[
[x: 245, c: [0, 0, 1, 1]], --get all nice colors back for other users
[x: 247, c: [0, 1, 0, 1]],
[x: 249, c: [0, 1, 1, 1]],
[x: 250, c: [1, 0, 1, 1]],
[x: 251, c: [1, 1, 0, 1]],
[x: 252, c: [1, 1, 1, 1]], --white as opposite to 1+2
[x: 254, c: [1, 0, 0, 1]],
IF whiteOutLine THEN Exception[x: 255, c: [1, 1, 1, 1]]
ELSE Exception[x: 255, c: [0, 0, 0, 1]] --all bits set
];
END;
Init8Patterns: Buttons.ButtonProc =
--all parameter are discarded
BEGIN
nDiffColor: CARDINAL = 1;
pDiffColor: CARDINAL = 2;
met1Color: CARDINAL = 4;
met2Color: CARDINAL = 8;
polyColor: CARDINAL = 16;
nWellColor: CARDINAL = 32;
pWellColor: CARDINAL = 64;
black: CARDINAL = 127;
Checker8: PROC[col1, col2: CARDINAL] RETURNS[REF CDColors.Brick] =
BEGIN
RETURN[NEW[CDColors.Brick ← [col1*256+col2, col2*256+col1, col1*256+col2, col2*256+col1]]]
END;
DefineDimmer8: PROC[lev: CD.Layer, color: CARDINAL] =
BEGIN
CDColors.DefineColor[lev, Checker8[color, 0], bit8, pushedOut];
END;
Full8: PROC[color: CARDINAL] RETURNS [b: REF CDColors.Brick] =
BEGIN
RETURN[Checker8[color, color]]
END;
CDColors.DefineColor[CMosB.ndif, Full8[nDiffColor], bit8];
CDColors.DefineColor[CMosB.pdif, Full8[pDiffColor], bit8];
CDColors.DefineColor[CMosB.met, Full8[met1Color], bit8];
CDColors.DefineColor[CMosB.met2, Full8[met2Color], bit8];
CDColors.DefineColor[CMosB.pol, Full8[polyColor], bit8];
CDColors.DefineColor[CMosB.nwell, Full8[nWellColor], bit8];
CDColors.DefineColor[CMosB.pwell, Full8[pWellColor], bit8];
CDColors.DefineColor[CMosB.cut, Full8[black], bit8];
CDColors.DefineColor[CMosB.cut2, Checker8[black, met1Color], bit8];
CDColors.DefineColor[CMosB.bur, Checker8[black, 0], bit8];
CDColors.DefineColor[CMosB.nwellCont, Checker8[pDiffColor, met1Color], bit8];
CDColors.DefineColor[CMosB.pwellCont, Checker8[nDiffColor, met1Color], bit8];
CDColors.DefineColor[CMosB.ovg, Checker8[met1Color, met2Color], bit8];
CDColors.DefineColor[CMosB.imp, NEW[CDColors.Brick←[14, 14, 0, 0]], bit8];
CDColors.DefineColor[CD.backgroundLayer, NEW[CDColors.Brick ← [8, 0, 256*8, 0]], bit8];
CDColors.DefineColor[CD.errorLayer, NEW[CDColors.Brick ← [255, 0, 0, 0]], bit8];
CDColors.DefineColor[CD.shadeLayer, NEW[CDColors.Brick←[0, 255,0 , 255]], bit8];
DefineDimmer8[CMosB.ndif, nDiffColor];
DefineDimmer8[CMosB.pdif, pDiffColor];
DefineDimmer8[CMosB.met, met1Color];
DefineDimmer8[CMosB.met2, met2Color];
DefineDimmer8[CMosB.pol, polyColor];
DefineDimmer8[CMosB.nwell, nWellColor];
DefineDimmer8[CMosB.cut, black];
DefineDimmer8[CMosB.cut2, black];
-- no change for bur, nwelCont, pwelCont, and ovgc for this background
END;
HackContextColors: PROC [] =
BEGIN
SetFilter[CMosB.ndif, 1];
SetFilter[CMosB.pdif, 2];
SetFilter[CMosB.met, 3];
SetFilter[CMosB.met2, 4];
SetFilter[CMosB.pol, 5];
SetFilter[CMosB.nwell, 6];
SetFilter[CMosB.pwell, 7];
END;
--XXXXX end technology XXXXXXXXXXXXXXXXXXXXX
whiteOutLine: BOOL;
currentColor: REF ColorDescRep = NEW[ColorDescRep];
curLayer: INT ← 1;
prevLayer: INT ← 1; -- to deselect the previous layer button
CoefSlider: TYPE = RECORD [
value: Labels.Label ← NIL,
select: Buttons.Button ← NIL,
bar: Sliders.Slider ← NIL
];
ColorSlider: TYPE = RECORD [
value: Labels.Label ← NIL,
bar: Sliders.Slider ← NIL
];
Handle: TYPE = REF AdjustColorRec;
AdjustColorRec: TYPE = RECORD [
outer: Containers.Container ← NIL,
coefLayer: ARRAY [0..numberOfColors) OF CoefSlider,
potarColor: ARRAY [0..3) OF ColorSlider,
height: CARDINAL ← 0  -- height measured from the top of the container
];
DataForSpecialProc: TYPE = REF DataForSpecialProcRec;
DataForSpecialProcRec: TYPE = RECORD [
my: Handle,
layer: CARDINAL
];
caption: Rope.ROPE = Rope.Concat[technologyName, " 8 bit color editor"];
ColorModeIs8: PROC [] RETURNS [BOOL] = INLINE
BEGIN
RETURN [terminal.hasColorDisplay AND ~terminal.GetColorMode[].full AND terminal.GetColorMode[].bitsPerPixelChannelA=8];
END;
Check8BitMode: PROC [] RETURNS [ok: BOOL] =
BEGIN
ok ← ColorModeIs8[];
IF ~ok THEN
TRUSTED{Process.Detach[FORK TerminalIO.PutRope["Put the display in 8 bit per pixel mode first\n"]]};
END;
UpdateWeightSlider: Sliders.SliderProc =
BEGIN
datac: DataForSpecialProc ← NARROW[clientData];
my: Handle ← datac.my;
layer: CARDINAL ← datac.layer;
IF ~ColorModeIs8[] THEN RETURN;
currentColor[layer].w ← value;
ComputeColors[];
Labels.Set[my.coefLayer[layer].value, Convert.RopeFromInt[Real.Fix[value*100]]];
END;
UpdateColorSlider: Sliders.SliderProc =
BEGIN
data: DataForSpecialProc ← NARROW[clientData];
my: Handle ← data.my;
layer: CARDINAL ← data.layer;
IF ~ColorModeIs8[] THEN RETURN;
Labels.Set[my.potarColor[layer].value, Convert.RopeFromInt[Real.Fix[value*100]]];
SELECT layer FROM
0 => currentColor[curLayer-1].r ← value;
1 => currentColor[curLayer-1].g ← value;
2 => currentColor[curLayer-1].b ← value;
ENDCASE => ERROR;
ComputeColors[];
END;
ResetButton: Buttons.ButtonProc =
BEGIN
Init8Patterns[parent: parent, clientData: clientData];
ResetMapButton[parent: parent, clientData: clientData];
END;
XSelection: Buttons.ButtonProc =
BEGIN
whiteOutLine ← ~whiteOutLine;
ComputeColors[];
END;
ResetMapButton: Buttons.ButtonProc =
--Resets color map;
--does not touch patterns
BEGIN
UpdateAllSliders: PROC [my: Handle] =
BEGIN
w: Sliders.NormalizedSliderValue;
IF my=NIL OR my.outer=NIL OR my.outer.destroyed THEN RETURN;
FOR layer: INT IN [0..numberOfColors) DO
w ← currentColor[layer].w;
Sliders.SetContents[my.coefLayer[layer].bar, 0];
Sliders.SetContents[my.coefLayer[layer].bar, w];
Labels.Set[my.coefLayer[layer].value,
  Convert.RopeFromInt[Real.Fix[currentColor[layer].w*100]]];
ENDLOOP;
ButtonProcLayerInternal[0, my];
END;
--ResetMapButton
IF Check8BitMode[] THEN {
ReadProfileColors[];
ComputeColors[];
IF clientData#NIL THEN UpdateAllSliders[NARROW[clientData, Handle]];
};
END;
ButtonProcLayer: Buttons.ButtonProc =
BEGIN
data: DataForSpecialProc ← NARROW[clientData];
IF Check8BitMode[] THEN {
ButtonProcLayerInternal[data.layer, data.my];
}
END;
ButtonProcLayerInternal: PROC[layer: CARDINAL, my: Handle] =
BEGIN
SetPotar: PROC[layer: CARDINAL, my: Handle] =
BEGIN
Sliders.SetContents[my.potarColor[0].bar, currentColor[layer].r];
Sliders.SetContents[my.potarColor[1].bar, currentColor[layer].g];
Sliders.SetContents[my.potarColor[2].bar, currentColor[layer].b];
Labels.Set[my.potarColor[0].value, Convert.RopeFromInt[Real.Fix[currentColor[layer].r*100]]];
Labels.Set[my.potarColor[1].value, Convert.RopeFromInt[Real.Fix[currentColor[layer].g*100]]];
Labels.Set[my.potarColor[2].value, Convert.RopeFromInt[Real.Fix[currentColor[layer].b*100]]];
END;
-- ButtonProcLayerInternal
curLayer ← layer+1;
SetPotar[layer, my];
Buttons.SetDisplayStyle[my.coefLayer[prevLayer-1].select, $BlackOnWhite];
Buttons.SetDisplayStyle[my.coefLayer[curLayer-1].select, $WhiteOnBlack];
prevLayer ← curLayer;
END;
CreateAnInstance: PROC[] =
--create an icon of the tool;
--does not change any color setup at initialization of the tool
BEGIN
nameColorSlider: ARRAY [0..3) OF Rope.ROPE ← ["R", "G", "B"];
coefX: CARDINAL = 10;
coefDX: CARDINAL = 25;
coefDeltaX: CARDINAL = coefDX+4;
coefSelectY: CARDINAL = 3;
coefSelectDY: CARDINAL = 15;
coefValueY: CARDINAL = coefSelectY+coefSelectDY+3;
coefValueDY: CARDINAL = 15;
coefBarY: CARDINAL = coefValueY+coefValueDY;
firstButtonY: CARDINAL = 3;
buttonX: CARDINAL = coefX+numberOfColors*coefDeltaX+8;
buttonDX: CARDINAL = 80;
firstSliderY: CARDINAL = 6;
sliderDY: CARDINAL = 22;
interSliderY: CARDINAL = 6;
labelX: CARDINAL = buttonX+buttonDX+10; -- R, G, B
labelDX: CARDINAL = 14;
label2X: CARDINAL = labelX+labelDX; -- number
label2DX: CARDINAL = 30;
sliderX: CARDINAL = label2X+label2DX;
my: Handle = NEW[AdjustColorRec];
buttonWy: CARDINAL ← firstButtonY;
NextButton: PROC [name: Rope.ROPE, proc: Buttons.ButtonProc] =
BEGIN
buttonDY: CARDINAL = 15;
interButtonY: CARDINAL = 2;
[] ← Buttons.Create[
info: [name: name,
wx: buttonX,
wy: buttonWy,
wh: buttonDY,
ww: buttonDX,
parent: my.outer,
border: TRUE],
proc: proc,
clientData: my,
fork: TRUE
]; 
buttonWy ← buttonWy+buttonDY+interButtonY;
END;
IF ~ColorModeIs8[] THEN
TerminalIO.PutRope["set display in 8 bit mode first\n"];
IF ViewerOps.FindViewer[caption]#NIL THEN {
TerminalIO.PutRopes[caption, " viewer already exits\n"];
RETURN
};
my.outer ← Containers.Create[[
name: caption,
iconic: TRUE,
icon: Icons.NewIconFromFile[Rope.Cat[CDEnvironment.GetWorkingDirectory[NIL], "Chipndale.icons"], 2],
column: left,
scrollable: FALSE
]];
my.height ← firstSliderY+3*(sliderDY+interSliderY);
-- create sliders for weights
FOR curLayer: INT IN [0..numberOfColors) DO
wInit: REAL ← currentColor[curLayer].w;
my.coefLayer[curLayer].select ← Buttons.Create[
info: [
name: cDefaults[curLayer].name,
iconic: TRUE,
wx: coefX+curLayer*coefDeltaX,
wy: coefSelectY,
wh: coefSelectDY,
ww: coefDX,
parent: my.outer,
border: TRUE],
proc: ButtonProcLayer,
clientData: NEW[DataForSpecialProcRec ← [my: my, layer: curLayer]]
]; 
my.coefLayer[curLayer].value ← Labels.Create[ [
name: Convert.RopeFromInt[Real.Fix[wInit*100]], -- initial contents
iconic: TRUE,
wx: coefX+curLayer*coefDeltaX,
wy: coefValueY,
wh: coefValueDY,
ww: coefDX,
parent: my.outer,
border: TRUE
]];
my.coefLayer[curLayer].bar ← Sliders.Create[
info: [
name: cDefaults[curLayer].name,
iconic: TRUE,
parent: my.outer,
wx: coefX+curLayer*coefDeltaX,
wy: coefBarY,
wh: -5, --guardY
ww: coefDX,
border: TRUE],
sliderProc: UpdateWeightSlider,
orientation: vertical,
value: wInit,
clientData: NEW[DataForSpecialProcRec ← [my: my, layer: curLayer]
]];
Containers.ChildYBound[my.outer, my.coefLayer[curLayer].bar];
ENDLOOP;
-- create command buttons
NextButton["reset all", ResetButton];
NextButton["reset map", ResetMapButton];
NextButton["reset pattern", Init8Patterns];
NextButton["X Selection", XSelection];
-- create sliders for rgb colors
FOR iColor: CARDINAL IN [0..3) DO
cInit: REALSELECT iColor FROM
0 => currentColor[curLayer-1].r,
1 => currentColor[curLayer-1].g,
2 => currentColor[curLayer-1].b,
ENDCASE => ERROR;
[] ← Labels.Create[
info: [name: nameColorSlider[iColor],
parent: my.outer,
wx: labelX,
wy: firstSliderY+iColor*(sliderDY+interSliderY),
ww: labelDX,
wh: sliderDY,
border: TRUE
]];
my.potarColor[iColor].bar ← Sliders.Create[
info: [parent: my.outer,
wx: sliderX,
wy: firstSliderY+iColor*(sliderDY+interSliderY),
ww: 10,
wh: sliderDY],
sliderProc: UpdateColorSlider,
orientation: horizontal,
value: cInit,
clientData: NEW[DataForSpecialProcRec ← [my: my, layer: iColor]]
];
Containers.ChildXBound[my.outer, my.potarColor[iColor].bar];
my.potarColor[iColor].value ← Labels.Create[[
name: Convert.RopeFromInt[Real.Fix[cInit*100]], -- initial contents
wx: label2X,
wy: firstSliderY+iColor*(sliderDY+interSliderY),
ww: label2DX,
wh: sliderDY,
parent: my.outer,
border: TRUE
]];
ENDLOOP;
ViewerOps.SetOpenHeight[my.outer, my.height]; -- hint our desired height
ViewerOps.PaintViewer[my.outer, all];    -- reflect above change
Buttons.SetDisplayStyle[my.coefLayer[0].select, $WhiteOnBlack];
END;
SaturationComm: CDSequencer.CommandProc =
BEGIN
sat: CARDINAL ← 1;
IF Check8BitMode[] THEN {
list: LIST OF Rope.ROPELIST["from profile"];
FOR i: INT DECREASING IN [0..numberOfColors) DO
list ← CONS[cDefaults[i].name, list]
ENDLOOP;
curLayer ← PopUpSelection.Request[header: "Layers", choice: list];
IF curLayer=0 THEN RETURN[];
IF curLayer<=numberOfColors THEN {
sat ← PopUpSelection.Request[
header: "Values",
choice: LIST["Invisible", "1", "2", "3", "4", "5", "6", "7", "8", "9", "Saturated"]
];
IF sat = 0 THEN RETURN[];
currentColor[curLayer-1].w ← Real.Float[sat-1]/10;
}
ELSE ReadProfileColors[];
ComputeColors[];
};
END;
specials1: LIST OF Exception ← NIL; --specials in the range of stipples [use for any comb]
specials2: LIST OF Exception ← NIL; --specials in the range of stipples [use for exact comb]
specials3: LIST OF Exception ← NIL; --specials to be considered later
ComputeColors: PROC[] =
--takes a color setting (weights and rgb values) and sets up the device
BEGIN
ComposeColor: PROC[c1, wc2: ColorRec] RETURNS [c: ColorRec] =
--c1 not weighted; wc2 weighted
INLINE BEGIN
w: REAL ← wc2.w;
c ← [c1.r+w*wc2.r, c1.g+w*wc2.g, c1.b+w*wc2.b, c1.w+w]
END;
SetMap: PROC[index: CARDINAL, color: ColorRec] =
BEGIN
RtoC: PROC [r: REAL] RETURNS [c: [0..256)] = INLINE {
c ← MAX[MIN[Real.Round[r*255], 255], 0]
};
mapEntrie: ImagerColorMap.MapEntry ← [index, RtoC[color.r], RtoC[color.g], RtoC[color.b]];
mapEntries ← CONS[mapEntrie, mapEntries];
END;
mapEntries: LIST OF ImagerColorMap.MapEntry ← NIL;
c: ColorRec;
bits: CARDINAL ← numberOfNormalColors;
power: CARDINAL ← Basics.BITSHIFT[1, bits];
IF ~ColorModeIs8[] THEN RETURN;
SetupSpecials[];
--colors in the range of layers
FOR i: CARDINAL IN [0..power) DO
c ← [0, 0, 0, 0];
--bit loop
FOR bit: CARDINAL IN [0..bits) DO
IF Basics.BITAND[i, Basics.BITSHIFT[1, bit]]#0 THEN {
c ← ComposeColor[c, currentColor[bit+1]];
};
ENDLOOP;
--check exception loops
FOR list: LIST OF Exception ← specials1, list.rest WHILE list#NIL DO
IF Basics.BITAND[i, list.first.x]=list.first.x THEN {
IF list.first.replace THEN c ← list.first.c
ELSE c ← ComposeColor[c, list.first.c];
};
ENDLOOP;
FOR list: LIST OF Exception ← specials2, list.rest WHILE list#NIL DO
IF i=list.first.x THEN {
IF list.first.replace THEN c ← list.first.c
ELSE c ← ComposeColor[c, list.first.c];
};
ENDLOOP;
--assign color
IF c.w<0.2 THEN c ← ComposeColor[c, currentColor[0]];
IF c.w=0 THEN c ← currentColor[0]
ELSE c ← [c.r/c.w, c.g/c.w, c.b/c.w, 0];
SetMap[i, c];
ENDLOOP;
--include further colors from exception loop
FOR list: LIST OF Exception ← specials3, list.rest WHILE list#NIL DO
IF list.first.x>=power THEN SetMap[list.first.x, list.first.c];
ENDLOOP;
ForceColors[mapEntries: mapEntries];
HackContextColors[];
END;
SetFilter: PROC [layer: CD.Layer, i: INT] = {
CDColors.DefineIColor[layer: layer,
col: ImagerColor.ColorFromRGB[[R: currentColor[i].r, G: currentColor[i].g, B: currentColor[i].b]],
display: bit8
];
};
ReadProfileColors: PUBLIC PROC [] =
BEGIN
DefaultColor: PROC [def: ColorRec] RETURNS [c: ColorRec] =
BEGIN
r: Rope.ROPE ← Rope.Cat["ChipNDale.", technologyName, ".", def.name];
c.w ← Real.Float[UserProfile.Number[key: r.Concat[".w"], default: Real.Round[def.w*100]]]/100;
c.r ← Real.Float[UserProfile.Number[key: r.Concat[".r"], default: Real.Round[def.r*100]]]/100;
c.g ← Real.Float[UserProfile.Number[key: r.Concat[".g"], default: Real.Round[def.g*100]]]/100;
c.b ← Real.Float[UserProfile.Number[key: r.Concat[".b"], default: Real.Round[def.b*100]]]/100;
END;
FOR i: INT IN [0..numberOfColors) DO
currentColor[i] ← DefaultColor[cDefaults[i]];
ENDLOOP;
whiteOutLine ← UserProfile.Boolean[
key: Rope.Cat["ChipNDale." , technologyName, ".WhiteOutline"],
default: TRUE
];
END;
ColorCommand: Commander.CommandProc = {
CreateAnInstance[];
};
ColorComm: PROC [comm: CDSequencer.Command] = {
CreateAnInstance[];
};
Init8Patterns[NIL];
ReadProfileColors[];
IF UserProfile.Boolean[Rope.Cat["ChipNDale.", technologyName, ".StartColorMap8"], ~Rope.Equal[technologyName, "chipnsil", FALSE]] THEN ComputeColors[];
CDSequencer.ImplementCommand[$SaturationComm, SaturationComm, technology, doQueue];
CDSequencer.ImplementCommand[$ColorTool, ColorComm, technology, dontQueue];
Commander.Register[
key: Rope.Cat["CD", technologyName, "8BitColors"],
proc: ColorCommand,
doc: Rope.Concat[technologyName, " 8 bit color tool"]
];
END.