ImagerStdColorDisplayImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Frank Crow, June 15, 1984 2:28:59 pm PDT
Last Edited by: Hiller, September 6, 1984 11:58:37 pm PDT
DIRECTORY
Imager    USING [XOR, Error],
ImagerBasic   USING [DeviceRectangle, Color, ColorRep, IntRectangle],
ImagerDisplay  USING [DisplayClass, DisplayClassRep, DisplayData, DisplayDataRep],
ImagerDisplayExtras USING [RGBSequence, ColorSequence],
ImagerMasks   USING [Mask, ApplyConstant, BoundingBox],
ImagerPixelMaps  USING [PixelMap, PixelMapRep, Function],
ColorModels   USING [Calibration, GetPhosphorCalibration],
ConstantColors  USING [ColorToRGB, white],
Terminal    USING [ColorMode, Current, GetColorBitmapState, GetColorMode,
         LegalColorMode, SetColor, SetColorBitmapState, Virtual,
         SetRedMap, SetGreenMap, SetBlueMap,
         WaitForBWVerticalRetrace, GetVisibility, SetVisibility],
TerminalExtras  USING [LockColorFrame, UnlockColorFrame],
Interminal   USING [TurnOnColorCursor],
Basics     USING [LowHalf, BITSHIFT, logBitsPerWord, LongMult],
Atom     USING [MakeAtom, PutPropOnList, GetPropFromList],
VM     USING [Allocate, AddressForPageNumber, wordsPerPage, Pin, Unpin],
WindowManager USING [ScreenPos],
ColorDisplayFace USING [TurnOn],
ColorDisplayHeadDorado USING [RPtr, mcb, rpNIL, ChanCB, ChanCBPtr, first64K,
           screenwidth],
CountedVM   USING [Allocate, Pointer, Handle],
UserProfile   USING [Token, ProfileChangedProc, CallWhenProfileChanges],
Rope     USING [ROPE, Equal],
Real     USING [FixC],
ImagerStdColorDisplay;
ImagerStdColorDisplayImpl: CEDAR PROGRAM
IMPORTS Imager, ImagerMasks, ColorModels, ConstantColors,
   Terminal, TerminalExtras, Atom, UserProfile, Rope, Real, ColorDisplayFace, VM,
   CountedVM, Basics, ColorDisplayHeadDorado , Interminal
EXPORTS ImagerStdColorDisplay
SHARES ColorDisplayHeadDorado
~ BEGIN
This provides support for the Dorado color display hardware common to standard displays.
DisplayClass: TYPE ~ ImagerDisplay.DisplayClass;
DisplayClassRep: TYPE ~ ImagerDisplay.DisplayClassRep;
DisplayData: TYPE ~ ImagerDisplay.DisplayData;
DisplayDataRep: TYPE ~ ImagerDisplay.DisplayDataRep;
DeviceRectangle: TYPE ~ ImagerBasic.DeviceRectangle;
Color: TYPE ~ ImagerBasic.Color;
ColorRep: TYPE ~ ImagerBasic.ColorRep;
SampledColor: TYPE = REF ColorRep.sampled;
ConstantColor: TYPE = REF ColorRep.constant;
SpecialColor: TYPE = REF ColorRep.special;
Mask: TYPE ~ ImagerMasks.Mask;
on: BOOLEAN ~ TRUE;
off: BOOLEAN ~ FALSE;
ColorDisplayError: PUBLIC SIGNAL [reason: ATOM] ~ CODE;
pinnedDataA, pinnedDataB: DisplayData ← NIL;
fullColor: BOOLEANFALSE;
storage, limit: ColorDisplayHeadDorado.RPtr ← ColorDisplayHeadDorado.rpNIL;
marginOffset: CARDINAL;
leftOverBytes: NAT;
pixelsPerInch: NAT;
displayResolution: {low, high};
displaySide: WindowManager.ScreenPos;
colorCalibration: ColorModels.Calibration;
GetDisplay: PROC[ vt: Terminal.Virtual, onMode: ATOM, mode: Terminal.ColorMode,
      lgBitsPerPixel: NAT] ~ {
displayMode: Terminal.ColorMode ~ vt.GetColorMode;
IF NOT vt.hasColorDisplay THEN ERROR ColorDisplayError[$NoColorDisplay];
IF (onMode = $reSet) OR (vt.GetColorBitmapState = none) THEN { -- setting up new display
IF NOT vt.LegalColorMode[mode]
THEN ERROR ColorDisplayError[$DisplayModeNotSupported];
[] ← vt.SetColorBitmapState[$displayed, mode, $none];  -- turn on display
TRUSTED {
Interminal.TurnOnColorCursor[        -- turn on cursor
IF mode.full THEN 24 ELSE mode.bitsPerPixelChannelA,     -- bits per pixel
displaySide = left               -- true => on left
]; };
IF onMode = $reSet THEN onMode ← IF lgBitsPerPixel > 3 THEN $all ELSE $aOn;
}
ELSE IF                 -- display previously in use
 (((onMode = $aOn) OR (onMode = $all))
 AND (displayMode.bitsPerPixelChannelA # mode.bitsPerPixelChannelA))
OR
 (((onMode = $bOn) OR (onMode = $all))
AND (displayMode.bitsPerPixelChannelB # mode.bitsPerPixelChannelB))
THEN ERROR ColorDisplayError[$IncompatibleDisplayMode];
SELECT onMode FROM
$aOn => IF vt.GetVisibility = bOnly OR vt.GetVisibility = all THEN vt.SetVisibility[all]
                   ELSE vt.SetVisibility[aOnly];
$bOn => IF vt.GetVisibility = aOnly OR vt.GetVisibility = all THEN vt.SetVisibility[all]
                   ELSE vt.SetVisibility[bOnly];
$all => vt.SetVisibility[all];
$none => vt.SetVisibility[none];
$unchanged => {};
ENDCASE => ERROR ColorDisplayError[$UndefinedOnMode];
};
Create: PUBLIC PROC [vt: Terminal.Virtual, mode: Terminal.ColorMode,
       displayClass: DisplayClass, creationData: REF,
       lgBitsPerPixel: NAT, setUpMapProc: ImagerStdColorDisplay.SetUpMapProc]
  RETURNS [displayData: DisplayData] ~ {
The creationData is inherited from the Create call. Here it is used to specify the dimensions of the pixel map and whether or not to pin it to the screen. NIL, defaults to using the hardware's display dimensions and pinning immediately.
GetPixelMap: PROC [x, y, width, height, lgBitsPerPixel: NAT, pointer: LONG POINTER ← NIL]
     RETURNS[pixelMap: ImagerPixelMaps.PixelMap] ~ {
pxpWd: NAT ← Basics.BITSHIFT[1, Basics.logBitsPerWord - lgBitsPerPixel];
wordsPerLine: NATIF width MOD pxpWd = 0 THEN width / pxpWd
                  ELSE width / pxpWd + 1;
words: INT ← Basics.LongMult[wordsPerLine, height + 1];
storage: CountedVM.Handle ← NIL;
IF pointer = NIL THEN {
storage ← CountedVM.Allocate[words: words];
TRUSTED { pointer ← CountedVM.Pointer[storage]; };
};
pixelMap [
sOrigin: y, fOrigin: x, sMin: 0, fMin: 0,
sSize: height,
fSize: width,
refRep: NEW [ImagerPixelMaps.PixelMapRep ← [
ref: storage,
pointer: pointer,
words: words,
lgBitsPerPixel: lgBitsPerPixel,
rast: wordsPerLine,
lines: height
]]
];
};
numMaps: NAT;
colorData: REF ImagerStdColorDisplay.ColorMapData;
pxpWd: NAT ← Basics.BITSHIFT[1, Basics.logBitsPerWord - lgBitsPerPixel];
creationList: LIST OF REF ANYNARROW[creationData, LIST OF REF ANY];
SELECT lgBitsPerPixel FROM -- 2 maps for 24bit color, 4 for 32bit color
4    => numMaps ← 2;
5    => numMaps ← 4;
ENDCASE => numMaps ← 1;
displayData ← NEW[DisplayDataRep[numMaps]];  -- Make a rep
displayData.displayClass ← displayClass;
Pixels/inch. Depends on display size and # of lines, from User.Profile in Init[]
displayData.xRes ← pixelsPerInch * displayClass.viewUnitsPerPixel;
displayData.yRes ← pixelsPerInch * displayClass.viewUnitsPerPixel;
displayData.rotate ← TRUE;
IF creationList = NIL THEN {      -- use default pixel map
[] ← GetDisplay[vt, $reSet, mode, lgBitsPerPixel];
displayData[0] ← GetPixelMap[0, 0, vt.colorWidth, vt.colorHeight, lgBitsPerPixel,
         vt.colorBitmapA];
displayData.props ← Atom.PutPropOnList[displayData.props, $PixelMapStatus, $OnAChannel];
IF numMaps > 1 THEN {
blueMap: NATIF numMaps = 4 THEN 2 ELSE 1;
displayData[blueMap] ← GetPixelMap[0, 0, vt.colorWidth, vt.colorHeight, 3,
           vt.colorBitmapB];
};
IF numMaps = 4 THEN {
displayData[1] ← GetPixelMap[0, 0, vt.colorWidth, vt.colorHeight, 3];  -- green pixels
displayData[3] ← GetPixelMap[0, 0, vt.colorWidth, vt.colorHeight, 3];  -- alpha pixels
};
IF pinnedDataA # NIL THEN IF NOT mode.full THEN
PinPixelMap[vt, displayData, FALSE, mode] --fix DCB chain
ELSE {            -- old display was full color
IF pinnedDataA # NIL THEN
VM.Unpin[ NARROW[pinnedDataA.pix[0].refRep.ref, CountedVM.Handle].interval ];
IF pinnedDataB # NIL THEN IF fullColor THEN
VM.Unpin[ NARROW[pinnedDataB.pix[1].refRep.ref, CountedVM.Handle].interval ]
ELSE
VM.Unpin[ NARROW[pinnedDataB.pix[0].refRep.ref, CountedVM.Handle].interval ];
pinnedDataA ← NIL; pinnedDataB ← NIL;
};
IF mode.full THEN fullColor ← TRUE ELSE fullColor ← FALSE;
}
ELSE { -- make new pixel map for 2-bit pixel maps (for overlays)
pinned: BOOLEANTRUE;
box: REF ImagerBasic.IntRectangle ← NIL;
WHILE creationList # NIL DO       -- pick up pin command, box, or both
WITH creationList.first SELECT FROM
pinTruth: REF BOOLEAN => pinned ← pinTruth^;
boxRef: REF ImagerBasic.IntRectangle => box ← boxRef;
ENDCASE;
creationList ← creationList.rest
ENDLOOP; 
IF box = NIL THEN box^ ← [0, 0, vt.colorWidth, vt.colorHeight];  -- use screen size
displayData[0] ← GetPixelMap[box.x, box.y, box.w, box.h, lgBitsPerPixel];
displayData.props ← Atom.PutPropOnList[displayData.props, $PixelMapStatus, $Allocated];
IF numMaps > 1 THEN {
blueMap: NATIF numMaps = 4 THEN 2 ELSE 1;
blueBits: LONG POINTER;   -- blue pixels lie above 16-bit interleaved RG pixels
TRUSTED {
blueBits ← displayData[0].refRep.pointer
   + Basics.LongMult[displayData[0].refRep.rast, box.h]
};
displayData[blueMap] ← GetPixelMap[box.x, box.y, box.w, box.h, 3, blueBits];
};
IF numMaps = 4 THEN {
displayData[1] ← GetPixelMap[box.x, box.y, box.w, box.h, 3];  -- green pixels
displayData[3] ← GetPixelMap[box.x, box.y, box.w, box.h, 3];  -- alpha pixels
};
IF pinned THEN {
PinPixelMap[vt, displayData, FALSE, mode];
IF mode.full THEN fullColor ← TRUE ELSE fullColor ← FALSE;
};
};
displayData.cachedColor ← NIL;
colorData ← NEW[ImagerStdColorDisplay.ColorMapData];
colorData.pixelValueList ← NIL;
colorData.colorCalibration ← colorCalibration;
colorData.nextEntry ← 0;
colorData.map ← NIL;
displayData.cachedColorData ← colorData;
setUpMapProc[displayData];
displayData.surfaceWidth ← displayData[0].fSize * displayClass.viewUnitsPerPixel;
displayData.surfaceHeight ← displayData[0].sSize * displayClass.viewUnitsPerPixel;
};
DoUnderLock: PUBLIC PROC [displayData: DisplayData, action: PROC, rectangle: DeviceRectangle] ~ {
vt: Terminal.Virtual ← Terminal.Current[];
TerminalExtras.LockColorFrame[
vt: vt,
xmin: MAX[rectangle.fMin, 0],
ymin: MAX[rectangle.sMin, 0],
xmax: MAX[rectangle.fMin+rectangle.fSize, 0],
ymax: MAX[rectangle.sMin+rectangle.sSize, 0]
];
action[! UNWIND => {TerminalExtras.UnlockColorFrame[vt]}];
TerminalExtras.UnlockColorFrame[vt];
};
ApplyMask: PUBLIC PROC [displayData: DisplayData,
         color: Color, mask: Mask, sTranslate, fTranslate: INTEGER,
         cachedColorProc: ImagerStdColorDisplay.CachedColorProc] ~ {
LockedApplyMask: PROC ~ {
function: ImagerPixelMaps.Function ← [null, null];
IF color = Imager.XOR THEN {
color ← ConstantColors.white;
function ← [xor, null];
};
WITH color SELECT FROM
constantColor: ConstantColor => {
ImagerMasks.ApplyConstant[
mask: mask,
clipper: displayData.compositeClipper,
dest: displayData[separationNumber],
value: currentPixelValue,
function: function,
sTranslate: sTranslate,
fTranslate: fTranslate
];
};
ENDCASE => Imager.Error[$UnsupportedColorType];
};
separationNumber: NAT;
pixelValues: LIST OF CARDINAL;
currentPixelValue: CARDINAL;
colorData: REF ImagerStdColorDisplay.ColorMapData ← NARROW[displayData.cachedColorData];
bb: DeviceRectangle ← ImagerMasks.BoundingBox[mask];
bb.sMin ← bb.sMin + sTranslate;
bb.fMin ← bb.fMin + fTranslate;
IF color # displayData.cachedColor THEN cachedColorProc[displayData, color];
pixelValuescolorData.pixelValueList;
FOR separationNumber IN [0..displayData.numberOfSeparations) DO
currentPixelValue ← pixelValues.first; pixelValues ← pixelValues.rest;
IF Atom.GetPropFromList[displayData.props, $PixelMapStatus] = $OnAChannel THEN
DoUnderLock[displayData, LockedApplyMask, bb]
ELSE LockedApplyMask[];
ENDLOOP;
};
PinPixelMap: PUBLIC PROC [vt: Terminal.Virtual, data: DisplayData, overLay: BOOLEANFALSE,
         mode: Terminal.ColorMode] ~ {
Pin a pixel map to the color display, replacing whatever was there before. Overlay = TRUE causes the B-channel to be used. Overlays may have only 2 or 4 bits per pixel, 4 only if the A-channel (non-overlay) pixelmap has <= 4 bits per pixel.
turnOnNeeded: BOOLEANFALSE;
numChannels: NATIF mode.full THEN 2 ELSE 1;      -- check for full color
bitmapPtrA, bitmapPtrB: LONG POINTER;
Make sure display is on
IF (data[0].refRep.ref = NIL) AND (pinnedDataA # NIL) THEN -- this doesn't have own bits
GetDisplay[vt, $reSet, mode, data[0].refRep.lgBitsPerPixel]
ELSE IF overLay THEN {
IF (vt.GetVisibility = none) OR (vt.GetVisibility = aOnly)
THEN GetDisplay[vt, $bOn, mode, data.pix[0].refRep.lgBitsPerPixel]
}
ELSE IF NOT mode.full THEN {
IF (vt.GetVisibility = none) OR (vt.GetVisibility = bOnly)
THEN GetDisplay[vt, $aOn, mode, data.pix[0].refRep.lgBitsPerPixel];
}
ELSE {   -- full color, reset if not previously full color
IF fullColor THEN GetDisplay[vt, $all, mode, 4] ELSE GetDisplay[vt, $reSet, mode, 4];
};
Do for both channels if full color, otherwise do for channel indicated by "overLay"
FOR i: NAT IN [0 .. numChannels) DO
logPixelsPerWord: NAT ← Basics.logBitsPerWord - data.pix[i].refRep.lgBitsPerPixel;
bChannel: BOOLEAN ← overLay OR (i = 1);   -- put on B if overlay or 2nd pixelmap
Catch negative origins and bite into pixelmap
bitmapPtr: LONG POINTER;
widthChange, heightChange: INTEGER ← 0;
TRUSTED {
IF data.pix[i].sOrigin >= 0 THEN bitmapPtr ← data.pix[i].refRep.pointer
ELSE {
bitmapPtr ← data.pix[i].refRep.pointer
    + Basics.LongMult[-data.pix[i].sOrigin, data.pix[i].refRep.rast];
heightChange ← data.pix[i].sOrigin;
};
IF data.pix[i].fOrigin < 0 THEN {
ptrChange: NAT ← Basics.BITSHIFT[
       Basics.BITSHIFT[ -data.pix[i].fOrigin, -(logPixelsPerWord+1)],
       1];
bitmapPtr ← bitmapPtr + ptrChange;
widthChange ← -Basics.BITSHIFT[ ptrChange, logPixelsPerWord];
};
};
IF bChannel THEN bitmapPtrB ← bitmapPtr ELSE bitmapPtrA ← bitmapPtr;
Set Null DCB to move down proper distance from top of screen
SetDCB[ aChannel: NOT bChannel,
   bitmap: bitmapPtr,
   leftMargin: 0,
   width: 0,
   height: MAX[0, data.pix[i].sOrigin],
   wordsPerLine: 0, 
   whichLink: 0
  ];
Make sure pinned storage held by ImagerStdColorDisplayImpl is released
IF bChannel THEN {
IF pinnedDataB # NIL THEN IF fullColor
THEN VM.Unpin[ NARROW[pinnedDataB.pix[i].refRep.ref, CountedVM.Handle].interval ]
ELSE VM.Unpin[ NARROW[pinnedDataB.pix[0].refRep.ref, CountedVM.Handle].interval ];
}
ELSE IF pinnedDataA # NIL THEN
VM.Unpin[ NARROW[pinnedDataA.pix[i].refRep.ref, CountedVM.Handle].interval ];
Pin VM pages for pixelmap
IF data.pix[i].refRep.ref # NIL -- refRep = NIL means this is Terminal.mesa's color memory
THEN VM.Pin[ NARROW[data.pix[i].refRep.ref, CountedVM.Handle].interval ];
Set DCB representing pixelmap
SetDCB[ aChannel: NOT bChannel,
   bitmap: bitmapPtr,
   leftMargin: MAX[0, data.pix[i].fOrigin],
   width: MAX[0,
       Basics.BITSHIFT[ data.pix[i].refRep.rast, logPixelsPerWord]
       + widthChange],
   height: MAX[0, data.pix[i].refRep.lines + heightChange],
   wordsPerLine: data.pix[i].refRep.rast,
   whichLink: 1
  ];
Set flags showing whether Terminal or ImagerStdColorDisplay got the bits
IF NOT bChannel THEN {
IF pinnedDataA = NIL THEN turnOnNeeded ← TRUE;   -- flag need to unpin display
IF data.pix[0].refRep.ref # NIL THEN pinnedDataA ← data ELSE pinnedDataA ← NIL;
}
ELSE {     -- Overlay or full color
IF data.pix[0].refRep.ref # NIL THEN pinnedDataB ← data ELSE pinnedDataB ← NIL;
};
ENDLOOP;
Turn off and unpin standard display if in use
IF turnOnNeeded THEN {
[] ← vt.SetColorBitmapState[$allocated, mode, vt.GetVisibility];
TRUSTED { ColorDisplayFace.TurnOn[]; };  -- Sneak by TerminalImpl, get display on
Reset bitmap pointer, etc. for color cursor
vt.colorBitmapA ← bitmapPtrA; vt.colorWordsPerLineA ← data.pix[0].refRep.rast;
vt.colorWidth ← data.pix[0].fSize; vt.colorHeight ← data.pix[0].sSize;
IF mode.full THEN {   -- Reset bitmap pointer, etc. for full color cursor 
vt.colorBitmapB ← bitmapPtrB; vt.colorWordsPerLineB ← data.pix[1].refRep.rast;
};
};
Update displayData to show it is on screen, leave ref to displayed data in global
IF overLay
THEN {
data.props ← Atom.PutPropOnList[data.props, $PixelMapStatus, $OnBChannel];
}
ELSE {
data.props ← Atom.PutPropOnList[data.props, $PixelMapStatus, $OnAChannel];
IF NOT mode.full THEN DownLoadColorMap[vt, data];   -- get map if pseudcolor
};
};
ReleasePixelMap: PUBLIC PROC [vt: Terminal.Virtual, data: DisplayData] ~ {
Remove a pixel map from the color display
IF Atom.GetPropFromList[data.props, $PixelMapStatus] = $OnAChannel
THEN { DeleteDCB[TRUE, 1];
   data.props ← Atom.PutPropOnList[data.props, $PixelMapStatus, $Allocated];
   pinnedDataA ← NIL;
  }
ELSE IF Atom.GetPropFromList[data.props, $PixelMapStatus] = $OnBChannel
THEN { DeleteDCB[FALSE, 1];
   data.props ← Atom.PutPropOnList[data.props, $PixelMapStatus, $Allocated];
   pinnedDataB ← NIL;
  }
ELSE SIGNAL ColorDisplayError[$UnDisplayedPixelMap];
VM.Unpin[ NARROW[data.pix[0].refRep.ref, CountedVM.Handle].interval ];  -- release storage
};
DownLoadColorMap: PROC [vt: Terminal.Virtual, displayData: DisplayData] ~ {
list: LIST OF REF ANYLIST[displayData.cachedColorData];
LoadColorMap[vt, list];
};
LoadColorMap: PUBLIC PROC [vt: Terminal.Virtual, data: REF ANY] ~ {
start: NAT ← 0;
rgbEntries: REF ImagerDisplayExtras.RGBSequence ← NIL;
colorEntries: REF ImagerDisplayExtras.ColorSequence ← NIL;
colorData: REF ImagerStdColorDisplay.ColorMapData ← NIL;
colorCalibration: ColorModels.Calibration ← NIL;
list: LIST OF REF ANYNARROW[data, LIST OF REF ANY];
mode: Terminal.ColorMode ← vt.GetColorMode;
vt.WaitForBWVerticalRetrace[]; -- await vt selection and top of scan (to control update rate)
WHILE list # NIL DO
WITH list.first SELECT FROM  -- pick up map description
rgb: REF ImagerDisplayExtras.RGBSequence => rgbEntries ← rgb;
color: REF ImagerDisplayExtras.ColorSequence => colorEntries ← color;
colorMap: REF ImagerStdColorDisplay.ColorMapData => colorData ← colorMap;
calibration: ColorModels.Calibration => colorCalibration ← calibration;
nat: REF INTEGER => start ← nat^;
ENDCASE;
list ← list.rest;
ENDLOOP;
IF colorData # NIL THEN rgbEntries ← colorData.rgbMap;
IF colorEntries # NIL THEN {
rgbEntries ← NEW[ ImagerDisplayExtras.RGBSequence[colorEntries.length] ];
FOR i: NAT IN [0..colorEntries.length) DO
r, g, b: REAL;
[r, g, b] ← ConstantColors.ColorToRGB[colorEntries[i], colorCalibration];
rgbEntries[i].r ← Real.FixC[r * 255.0];
rgbEntries[i].g ← Real.FixC[g * 255.0];
rgbEntries[i].b ← Real.FixC[b * 255.0];
ENDLOOP;
};
IF rgbEntries # NIL THEN {
IF mode.full = FALSE           -- pseudocolor mapped display
THEN IF (mode.bitsPerPixelChannelB = 2) OR (mode.bitsPerPixelChannelB = 0)
THEN { FOR i: NAT DECREASING IN [start..rgbEntries.length+start) DO
index: NAT ← i MOD 256; table: NAT ← i / 256;    -- 4 tables each 256 entries
vt.SetColor[index, table, rgbEntries[i].r, rgbEntries[i].g, rgbEntries[i].b];
ENDLOOP; }
ELSE { FOR i: NAT DECREASING IN [start..rgbEntries.length+start) DO
index: NAT ← i MOD 16; table: NAT ← i / 16;    -- 16 tables each 16 entries
vt.SetColor[index, table, rgbEntries[i].r, rgbEntries[i].g, rgbEntries[i].b];
ENDLOOP ; }
ELSE FOR index: NAT IN [start..rgbEntries.length+start) DO  -- full-color 24-bit display
vt.SetRedMap[index, rgbEntries[index].r];
vt.SetGreenMap[index, rgbEntries[index].g];
vt.SetBlueMap[index, rgbEntries[index].b];
ENDLOOP;
};
};
SetDCB: PROC [aChannel: BOOLEANTRUE, bitmap: LONG POINTER,
     leftMargin, width, height, wordsPerLine, whichLink: NAT] ~ TRUSTED {
Alloc: PROC [words: CARDINAL] RETURNS [pointer: ColorDisplayHeadDorado.RPtr] ~ TRUSTED{
page, count: INT;
storageTop: ColorDisplayHeadDorado.RPtr ← storage + words;
IF (storage = ColorDisplayHeadDorado.rpNIL)
OR (LOOPHOLE[storageTop, CARDINAL] > LOOPHOLE[limit, CARDINAL])
THEN {
[[page, count]] ← VM.Allocate[count: 1, partition: lowCore, in64K: TRUE];
storage ← LOOPHOLE[Basics.LowHalf[LOOPHOLE[VM.AddressForPageNumber[page]]]];
limit ← storage + VM.wordsPerPage;
};
pointer ← storage;
storage ← storage + words;
};
link: NAT ← 0;
chainPtr, lastPtr: LONG POINTER TO ColorDisplayHeadDorado.ChanCB ← NIL;
 Check for color display MCB, then DCB chain
IF ColorDisplayHeadDorado.mcb = NIL THEN {
SIGNAL ColorDisplayError[$NoColorDisplay]; RETURN[]; };
IF aChannel THEN lastPtr ← chainPtr ←
      @ColorDisplayHeadDorado.first64K[ColorDisplayHeadDorado.mcb.achanCB]
    ELSE lastPtr ← chainPtr ←
      @ColorDisplayHeadDorado.first64K[ColorDisplayHeadDorado.mcb.bchanCB];
IF chainPtr = NIL THEN {
SIGNAL ColorDisplayError[$NoDCBChain]; RETURN[]; };
IF whichLink > 31 THEN { SIGNAL ColorDisplayError[$Over32DCBs]; RETURN[]; };
IF (wordsPerLine * 2 > leftOverBytes) THEN  -- check for excess leftovers at end of scan
IF (ColorDisplayHeadDorado.screenwidth - width) > leftOverBytes
THEN { SIGNAL ColorDisplayError[$ExcessLeftOverPixels]; RETURN[]; };
 Find desired link in DCB chain
WHILE (link < whichLink) AND NOT (chainPtr = NIL) DO
link ← link + 1;
lastPtr ← chainPtr;           -- step to next link
IF chainPtr.link = ColorDisplayHeadDorado.rpNIL
THEN chainPtr ← NIL
ELSE chainPtr ← @ColorDisplayHeadDorado.first64K[chainPtr.link];
ENDLOOP;
 Load desired link; Make new links if desired link lies beyond end of chain
WHILE link <= whichLink DO
relPtr: ColorDisplayHeadDorado.ChanCBPtr ← ColorDisplayHeadDorado.rpNIL;
IF chainPtr = NIL
THEN {   -- Make new DCB, if unlinked
relPtr ← Alloc[SIZE[ColorDisplayHeadDorado.ChanCB]];
chainPtr ← @ColorDisplayHeadDorado.first64K[relPtr];
chainPtr.link ← ColorDisplayHeadDorado.rpNIL;  -- make sure chain ends here
}
ELSE relPtr ← lastPtr.link;   -- in case chain is already linked (or zeroth link)
IF (link < whichLink) OR (width = 0)       -- null DCB
THEN {
chainPtr.wordsPerLine ← 0;
chainPtr.bitmap ← NIL;
chainPtr.linesPerField ← IF link = whichLink THEN height / 2 ELSE 0;
chainPtr.pixelsPerLine ← 0;
chainPtr.leftMargin ← LAST[NAT];
chainPtr.scan ← lastPtr.scan;
}
ELSE {
chainPtr.wordsPerLine ← wordsPerLine;     -- load visible DCB
chainPtr.bitmap ← bitmap;
chainPtr.linesPerField ← height / 2;
chainPtr.pixelsPerLine ← width + 255;
chainPtr.leftMargin ← leftMargin + marginOffset;
chainPtr.scan ← lastPtr.scan;
};
lastPtr.link ← relPtr;           -- link into DCB chain after loaded
lastPtr ← chainPtr;           -- step to next link
IF chainPtr.link = ColorDisplayHeadDorado.rpNIL
THEN chainPtr ← NIL
ELSE chainPtr ← @ColorDisplayHeadDorado.first64K[chainPtr.link];
link ← link + 1;
ENDLOOP;
};
DeleteDCB: PROC [aChannel: BOOLEANTRUE, whichLink: NAT] ~ TRUSTED {
link: NAT ← 0;
chainPtr, lastPtr: LONG POINTER TO ColorDisplayHeadDorado.ChanCB ← NIL;
IF ColorDisplayHeadDorado.mcb = NIL THEN {
SIGNAL ColorDisplayError[$NoColorDisplay]; RETURN[]; };
IF aChannel THEN lastPtr ← chainPtr ←
      @ColorDisplayHeadDorado.first64K[ColorDisplayHeadDorado.mcb.achanCB]
    ELSE lastPtr ← chainPtr ←
      @ColorDisplayHeadDorado.first64K[ColorDisplayHeadDorado.mcb.bchanCB];
IF chainPtr = NIL THEN {
SIGNAL ColorDisplayError[$NoDCBChain]; RETURN[]; };
 Find desired link in DCB chain
WHILE (link < whichLink) AND NOT (chainPtr = NIL) DO
link ← link + 1;
lastPtr ← chainPtr;           -- step to next link
IF chainPtr.link = ColorDisplayHeadDorado.rpNIL
THEN chainPtr ← NIL
ELSE chainPtr ← @ColorDisplayHeadDorado.first64K[chainPtr.link];
ENDLOOP;
 Delete Link, if found, note, no storage recovery
IF (link = whichLink) AND (chainPtr # NIL) THEN lastPtr.link ← chainPtr.link;
};
MonitorSpecs: UserProfile.ProfileChangedProc = TRUSTED {
Initialize: set left margin assuming monitor type from resolution (potentially wrong)
displayType: Rope.ROPE ← UserProfile.Token["ColorDisplay.Type", "640x480"];
SELECT TRUE FROM
Rope.Equal[displayType, "640x480", FALSE] => {
pixelsPerInch ← 64;
marginOffset ← 54;    -- Hitachi 13" monitors @480 lines
leftOverBytes ← 460;    -- Exceeding this causes unstable raster
displayResolution ← low;
};
Rope.Equal[displayType, "1024x768", FALSE] => {
pixelsPerInch ← 68;
marginOffset ← 71;    -- Conrac @768 lines (may be 0, actually)
leftOverBytes ← 1024;
displayResolution ← high;
};
ENDCASE => {      -- 640x480 is the default
pixelsPerInch ← 64;
marginOffset ← 54;
leftOverBytes ← 460;
displayResolution ← low;
};
displaySide ← IF Rope.Equal[UserProfile.Token["ColorDisplay.Side", "left"], "left", FALSE]
THEN left ELSE right;
colorCalibration ← ColorModels.GetPhosphorCalibration[
Atom.MakeAtom[UserProfile.Token["ColorDisplay.Calibration", "DefaultLP"] ]
];
};
{
UserProfile.CallWhenProfileChanges[MonitorSpecs];
};
END.