ColorDisplayHeadDorado.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Doug Wyatt, May 2, 1985 6:40:22 pm PDT
Russ Atkinson (RRA) June 17, 1985 4:38:05 pm PDT
DIRECTORY
Basics USING [BITAND, BITOR, BITSHIFT, bitsPerWord, LongDiv, LongMult],
ColorDisplayDefs USING [ChannelsVisible, ChannelValue, ColorDisplayType, ColorMode, ColorValue],
ColorDisplayDorado USING [AEntry, AIndex, ATable, Base, base0, BCDatum, BCTable, bMapRegister, ChannelControlBlock, ClockControl, cMapRegister, ColorControlBlock, ColorCSB, csb, Flags, HorizontalControl, MixerDatum, mixerRegister, MonitorControlBlock, pixelsPerLineOffset, RNIL, ScanControl, VerticalControl],
ColorDisplayFace USING [nullMode],
DeviceCleanup USING [Await, Item, Reason],
DoradoInputOutput USING [DMuxAddr, InputNoPE, IOAddress, MufManResult, Output, RWMufMan],
PrincOpsUtils USING [HighHalf, LowHalf],
ProcessorFace USING [GetClockPulses, microsecondsPerHundredPulses];
ColorDisplayHeadDorado: PROGRAM
IMPORTS Basics, DeviceCleanup, DoradoInputOutput, PrincOpsUtils, ProcessorFace
EXPORTS ColorDisplayFace
~ BEGIN OPEN ColorDisplayFace, ColorDisplayDefs, ColorDisplayDorado;
ErrorHalt: PROC ~ { ERROR };
Public interface variables (valid as soon as the head has been started)
displayType: PUBLIC ColorDisplayType ← none; -- display type, "none" if display not available
width: PUBLIC NAT ← 0; -- raster width in pixels
height: PUBLIC NAT ← 0; -- raster height in pixels
pixelsPerInch: PUBLIC NAT ← 0; -- approximate pixel size
globalStateSize: PUBLIC NAT
SIZE
[MonitorControlBlock]+2*SIZE[ChannelControlBlock]+SIZE[ColorControlBlock];
Internal globals.
oldRev: BOOLFALSE; -- TRUE if color board has an old revision level (less than Cj)
vControl: VerticalControl ← [0, 0, 0, 0];
hControl: HorizontalControl ← [0, 0, 0, 0];
clkControl: ClockControl ← [mul: 0, div: 0];
clkControlDouble: ClockControl ← [mul: 0, div: 0];
marginOffset: CARDINAL ← 0;
mcb: LONG POINTER TO MonitorControlBlock ← NIL;
channelA: LONG POINTER TO ChannelControlBlock ← NIL;
channelB: LONG POINTER TO ChannelControlBlock ← NIL;
color: LONG POINTER TO ColorControlBlock ← NIL;
currentMap: ColorMap ← NIL;
turnOnFlags: Flags ← [];
showA, showB: BOOLFALSE;
state: {uninitialized, disconnected, connected, displayed} ← uninitialized;
SetDisplayType: PUBLIC SAFE PROC [type: ColorDisplayType] RETURNS [ok: BOOL] ~ CHECKED {
SELECT type FROM
standard => {
width ← 640; -- must be a multiple of 32
height ← 480; -- must be a multiple of 2
pixelsPerInch ← 42; -- *** approximate ***
vControl ← [VBtoVS: 3B, VStoVS: 3B, VStoVB: 20B, VisibleLines: 240];
hControl ← [HRamMaxAddr: 379, HBLeadLength: 6, HSTrailAddr: 28, HBTrailLength: 25];
clkControl ← [mul: 130B, div: 14B];
clkControlDouble ← [mul: 130B, div: 16B];
marginOffset ← 65B;
};
highResolution => {
width ← 1024; -- must be a multiple of 32
height ← 768; -- must be a multiple of 2
pixelsPerInch ← 72; -- *** approximate ***
vControl ← [VBtoVS: 0, VStoVS: 3, VStoVB: 18, VisibleLines: 384];
hControl ← [HRamMaxAddr: 601, HBLeadLength: 3, HSTrailAddr: 50, HBTrailLength: 35];
clkControl ← [mul: 54, div: 14];
marginOffset ← 111;
};
ENDCASE => RETURN[FALSE];
displayType ← type;
RETURN[TRUE];
};
RP: PROC[p: LONG POINTER] RETURNS[Base RELATIVE POINTER] ~ INLINE {
RETURN[LOOPHOLE[PrincOpsUtils.LowHalf[p]]] -- assumes high half is zero
};
Initialize: PUBLIC PROC [globalState: LONG POINTER] = {
allocated: NAT ← 0; -- words allocated from globalState block
Allocate: PROC[size: NAT] RETURNS[LONG POINTER] ~ INLINE {
p: LONG POINTER ~ globalState+allocated;
allocated ← allocated+size; RETURN[p];
};
IF state#uninitialized THEN ErrorHalt[];
IF NOT SetDisplayType[displayType] THEN ErrorHalt[];
IF PrincOpsUtils.HighHalf[globalState]#0 THEN ErrorHalt[];
mcb ← Allocate[SIZE[MonitorControlBlock]];
channelA ← Allocate[SIZE[ChannelControlBlock]];
channelB ← Allocate[SIZE[ChannelControlBlock]];
color ← Allocate[SIZE[ColorControlBlock]];
IF allocated>globalStateSize THEN ErrorHalt[];
mcb^ ← [flags: [], channelA: RNIL, channelB: RNIL, color: RP[color]];
channelA^ ← channelB^ ← nullChannelControlBlock;
color^ ← [tableA: NIL, tableB: NIL, tableC: NIL, vc: vControl, hc: hControl, clk: clkControl];
state ← disconnected;
};
HasMode: PUBLIC SAFE PROC[mode: ColorMode] RETURNS[BOOL] ~ CHECKED {
a: NAT ~ mode.bitsPerPixelChannelA;
b: NAT ~ mode.bitsPerPixelChannelB;
IF displayType=none THEN RETURN[FALSE]; -- no color display
IF mode.full THEN RETURN[displayType=standard];
Not enough bandwidth to handle 24 bits per pixel at high resolution.
IF a=0 AND b=0 THEN RETURN[FALSE];
SELECT a FROM 0, 1, 2, 4, 8 => NULL ENDCASE => RETURN[FALSE];
SELECT b FROM 0, 1, 2, 4 => NULL ENDCASE => RETURN[FALSE];
IF (a+b)>10 THEN RETURN[FALSE];
IF oldRev AND a#0 AND b#0 THEN RETURN[FALSE];
Old revision boards can't run both channels at the same time.
RETURN[TRUE];
};
NextMode: PUBLIC SAFE PROC [mode: ColorMode] RETURNS [ColorMode] ~ CHECKED {
Next: SAFE PROC [mode: ColorMode] RETURNS [ColorMode] ~ CHECKED {
a: NAT ~ mode.bitsPerPixelChannelA;
b: NAT ~ mode.bitsPerPixelChannelB;
IF mode.full THEN RETURN[nullMode];
IF b<4 THEN RETURN[[FALSE, a, b*2]];
IF a<8 THEN RETURN[[FALSE, a*2, 0]];
RETURN[[TRUE, 0, 0]];
};
IF NOT(mode=nullMode OR HasMode[mode]) THEN RETURN[nullMode]; -- illegal mode
FOR next: ColorMode ← Next[mode], Next[next] UNTIL next=nullMode DO
IF HasMode[next] THEN RETURN[next];
ENDLOOP;
RETURN[nullMode];
};
nullScanControl: ScanControl ~ [mode24: FALSE, aChannelOnly: FALSE, bBypass: FALSE,
pixelMode: VAL[0], resolution: VAL[0], bitsPerPixel: 0];
nullChannelControlBlock: ChannelControlBlock ~ [link: RNIL, wordsPerLine: 0, address: NIL, linesPerField: 0, pixelsPerLine: 0, leftMargin: 0, scanControl: nullScanControl];
bitsPerWord: NAT ~ Basics.bitsPerWord;
Connect: PUBLIC PROC[mode: ColorMode, baseA, baseB: LONG POINTER, map: ColorMap] ~ {
vc: VerticalControl ← vControl;
hc: HorizontalControl ← hControl;
clk: ClockControl ← clkControl;
template: ChannelControlBlock ← nullChannelControlBlock;
template.linesPerField ← height/2;
template.pixelsPerLine ← width+pixelsPerLineOffset;
template.leftMargin ← marginOffset;
template.scanControl.resolution ← full;
IF mode.full THEN {
channelA fetches two pixel values per screen pixel
channelB runs at half resolution: 2 pixel clocks for each pixel from the frame buffer
template.scanControl.mode24 ← TRUE;
template.scanControl.aChannelOnly ← FALSE;
template.scanControl.bBypass ← TRUE;
template.scanControl.pixelMode ← a8b2;
clk ← clkControlDouble;
double the pixel clock rate
hc.HRamMaxAddr ← 2*hc.HRamMaxAddr;
hc.HBLeadLength ← 2*hc.HBLeadLength;
hc.HSTrailAddr ← 2*hc.HSTrailAddr;
hc.HBTrailLength ← 2*hc.HBTrailLength;
horizontal control is measured in pixel clock periods
template.leftMargin ← 2*marginOffset + 40B--HWindow-- + 40B--marginCounter--;
adjust leftMargin to compensate for the faster pixel clock
template.pixelsPerLine ← 2*width+pixelsPerLineOffset;
channelA^ ← template;
channelA.address ← baseA;
channelA.wordsPerLine ← (width*16)/bitsPerWord;
channelA.scanControl.bitsPerPixel ← 8;
channelB^ ← template;
channelB.address ← baseB;
channelB.wordsPerLine ← (width*8)/bitsPerWord;
channelB.scanControl.resolution ← half;
channelB.scanControl.bitsPerPixel ← 8;
color.tableA ← LOOPHOLE[@map.tableA];
color.tableB ← LOOPHOLE[@map.tableB];
color.tableC ← LOOPHOLE[@map.tableC];
turnOnFlags ← [vc: TRUE, hc: TRUE, clk: TRUE, a: TRUE, b: TRUE, c: TRUE];
}
ELSE {
bitsPerPixelA: NAT ~ mode.bitsPerPixelChannelA;
bitsPerPixelB: NAT ~ mode.bitsPerPixelChannelB;
template.scanControl.mode24 ← FALSE;
template.scanControl.aChannelOnly ← (bitsPerPixelB=0);
template.scanControl.bBypass ← FALSE;
template.scanControl.pixelMode ← (IF bitsPerPixelB>2 THEN a6b4 ELSE a8b2);
channelA^ ← channelB^ ← template;
IF bitsPerPixelA=0 THEN channelA.address ← NIL
ELSE {
channelA.address ← baseA;
channelA.wordsPerLine ← (width*bitsPerPixelA)/bitsPerWord;
channelA.scanControl.bitsPerPixel ← bitsPerPixelA;
};
IF bitsPerPixelB=0 THEN channelB.address ← NIL
ELSE {
channelB.address ← baseB;
channelB.wordsPerLine ← (width*bitsPerPixelB)/bitsPerWord;
channelB.scanControl.bitsPerPixel ← bitsPerPixelB;
};
color.tableA ← LOOPHOLE[@map.tableA];
color.tableB ← NIL;
color.tableC ← NIL;
turnOnFlags ← [vc: TRUE, hc: TRUE, clk: TRUE, a: TRUE];
};
color.vc ← vc;
color.hc ← hc;
color.clk ← clk;
currentMap ← map;
state ← connected;
};
setBlack: BOOLTRUE;
Disconnect: PUBLIC SAFE PROC = TRUSTED {
IF state=displayed THEN TurnOff[];
IF state#connected THEN ErrorHalt[];
IF setBlack THEN {
MixerOutput: PROC [datum: MixerDatum]
~ INLINE {DoradoInputOutput.Output[datum: LOOPHOLE[datum], register: mixerRegister]};
BMapOutput: PROC [datum: BCDatum]
~ INLINE {DoradoInputOutput.Output[datum: LOOPHOLE[datum], register: bMapRegister]};
CMapOutput: PROC [datum: BCDatum]
~ INLINE {DoradoInputOutput.Output[datum: LOOPHOLE[datum], register: cMapRegister]};
MixerOutput[[keep: T, body: null[]]];
MixerOutput[[keep: T, load: T, body: addr[addr: 0, select: lo]]];
MixerOutput[[keep: T, write: T, body: data[data: 0]]];
MixerOutput[[keep: T, load: T, body: addr[addr: 0, select: hi]]];
MixerOutput[[keep: T, write: T, body: data[data: 0]]];
MixerOutput[[keep: F, body: null[]]];
BMapOutput[[keep: T, body: null[]]];
BMapOutput[[keep: T, load: T, body: addr[addr: 0]]];
BMapOutput[[keep: T, write: T, body: data[data: 0]]];
BMapOutput[[keep: F, body: null[]]];
CMapOutput[[keep: T, body: null[]]];
CMapOutput[[keep: T, load: T, body: addr[addr: 0]]];
CMapOutput[[keep: T, write: T, body: data[data: 0]]];
CMapOutput[[keep: F, body: null[]]];
};
color.tableA ← NIL; color.tableB ← color.tableC ← NIL;
channelA.address ← channelB.address ← NIL;
currentMap ← NIL;
turnOnFlags ← [];
state ← disconnected;
};
TurnOn: PUBLIC PROC = {
IF state#connected THEN RETURN;
mcb.flags ← turnOnFlags;
mcb.channelA ← (IF showA AND channelA.address#NIL THEN RP[channelA] ELSE RNIL);
mcb.channelB ← (IF showB AND channelB.address#NIL THEN RP[channelB] ELSE RNIL);
base0[csb].mcb ← RP[mcb];
state ← displayed;
};
TurnOff: PUBLIC SAFE PROC = TRUSTED {
IF state#displayed THEN RETURN;
base0[csb].mcb ← RNIL;
Wait[]; -- be sure the microcode is no longer touching bitmaps or color tables
state ← connected;
};
SetVisibility: PUBLIC SAFE PROC [visibility: ChannelsVisible] ~ CHECKED {
SELECT visibility FROM
none => { showA ← showB ← FALSE };
aOnly => { showA ← TRUE; showB ← FALSE };
bOnly => { showA ← FALSE; showB ← TRUE };
all => { showA ← showB ← TRUE };
ENDCASE;
IF state=displayed THEN TRUSTED {
mcb.channelA ← (IF showA AND channelA.address#NIL THEN RP[channelA] ELSE RNIL);
mcb.channelB ← (IF showB AND channelB.address#NIL THEN RP[channelB] ELSE RNIL);
};
};
ColorMap: TYPE ~ LONG POINTER TO ColorMapRep;
ColorMapRep: PUBLIC TYPE ~ RECORD[
shiftA, shiftB: INTEGER ← 0,
maskA, maskB: WORD ← 0,
unused: ARRAY [4..256) OF WORDALL[0],
tableA: ATable ← ALL[[redH: 0, redL: 0, blue: 0, green: 0]],
tableB: BCTable ← ALL[[value: 0]],
tableC: BCTable ← ALL[[value: 0]]
];
wordsForColorMap: PUBLIC NATSIZE[ColorMapRep];
InitializeColorMap: PUBLIC PROC [mode: ColorMode, pointer: LONG POINTER]
RETURNS [ColorMap] ~ {
map: ColorMap ~ LOOPHOLE[pointer];
map^ ← [];
IF NOT mode.full THEN {
IF mode.bitsPerPixelChannelB>2 THEN {
map.shiftA ← 4+(6-mode.bitsPerPixelChannelA);
map.shiftB ← 4-mode.bitsPerPixelChannelB;
}
ELSE {
map.shiftA ← 2+(8-mode.bitsPerPixelChannelA);
map.shiftB ← 2-mode.bitsPerPixelChannelB;
};
map.maskA ← Basics.BITSHIFT[1, mode.bitsPerPixelChannelA]-1;
map.maskB ← Basics.BITSHIFT[1, mode.bitsPerPixelChannelB]-1;
};
RETURN[map];
};
SetColor: PUBLIC PROC[map: ColorMap, pixelA, pixelB: ChannelValue, r, g, b: ColorValue] ~ {
index: AIndex ~ Basics.BITOR[
Basics.BITSHIFT[Basics.BITAND[pixelA, map.maskA], map.shiftA],
Basics.BITSHIFT[Basics.BITAND[pixelB, map.maskB], map.shiftB]
];
entry: AEntry ~ [redH: r/16, redL: r MOD 16, green: g, blue: b];
map.tableA[index] ← entry;
IF map=currentMap AND state=displayed THEN mcb.flags.a ← TRUE;
};
GetColor: PUBLIC SAFE PROC[map: ColorMap, pixelA, pixelB: ChannelValue]
RETURNS [r, g, b: ColorValue] ~ TRUSTED {
index: AIndex ~ Basics.BITOR[
Basics.BITSHIFT[Basics.BITAND[pixelA, map.maskA], map.shiftA],
Basics.BITSHIFT[Basics.BITAND[pixelB, map.maskB], map.shiftB]
];
entry: AEntry ~ map.tableA[index];
RETURN[r: entry.redH*16+entry.redL, g: entry.green, b: entry.blue];
};
SetR: PUBLIC PROC[map: ColorMap, in: ChannelValue, out: ColorValue] ~ {
index: AIndex ~ Basics.BITSHIFT[in, 2];
entry: AEntry ~ [redH: out/16, redL: out MOD 16, green: 0, blue: 0];
map.tableA[index] ← entry;
IF map=currentMap AND state=displayed THEN mcb.flags.a ← TRUE;
};
SetG: PUBLIC PROC[map: ColorMap, in: ChannelValue, out: ColorValue] ~ {
map.tableC[in] ← [value: out];
IF map=currentMap AND state=displayed THEN mcb.flags.c ← TRUE;
};
SetB: PUBLIC PROC[map: ColorMap, in: ChannelValue, out: ColorValue] ~ {
map.tableB[in] ← [value: out];
IF map=currentMap AND state=displayed THEN mcb.flags.b ← TRUE;
};
GetR: PUBLIC SAFE PROC[map: ColorMap, in: ChannelValue]
RETURNS [out: ColorValue] ~ TRUSTED {
index: AIndex ~ Basics.BITSHIFT[in, 2];
entry: AEntry ~ map.tableA[index];
RETURN[entry.redH*16+entry.redL];
};
GetG: PUBLIC SAFE PROC[map: ColorMap, in: ChannelValue]
RETURNS [out: ColorValue] ~ TRUSTED {
RETURN[map.tableC[in].value];
};
GetB: PUBLIC SAFE PROC[map: ColorMap, in: ChannelValue]
RETURNS [out: ColorValue] ~ TRUSTED {
RETURN[map.tableB[in].value];
};
Initialization
Since the runtime support may be in a delicate state when the heads are started, this code avoids software-implemented operations like long integer multiply or divide.
hundredPulsesPerSecond: CARDINAL ~ Basics.LongDiv[
1000000, -- microsecondsPerSecond
ProcessorFace.microsecondsPerHundredPulses -- (assumed >15)
];
pulsesPerField: CARDINAL ~ Basics.LongDiv[
Basics.LongMult[hundredPulsesPerSecond, 100], -- pulsesPerSecond (assumed <3932160)
60 -- fieldsPerSecond (approximate)
];
waitPulses: LONG CARDINAL ~ Basics.LongMult[pulsesPerField, 3]; -- 3 fields, just to be sure
Wait: PROC ~ INLINE { -- inline, because device cleanup must not do procedure call
startPulses: LONG CARDINAL ~ ProcessorFace.GetClockPulses[];
WHILE (ProcessorFace.GetClockPulses[]-startPulses)<waitPulses DO ENDLOOP;
};
InitializeCleanup: PUBLIC PROC ~ {
item: DeviceCleanup.Item;
reason: DeviceCleanup.Reason;
csbState: ColorCSB;
DO
reason ← DeviceCleanup.Await[@item];
SELECT reason FROM
turnOff, kill => {
csbState ← base0[csb];
base0[csb].mcb ← RNIL;
Wait[];
};
turnOn => {
IF state=displayed THEN mcb.flags ← turnOnFlags;
base0[csb] ← csbState;
};
ENDCASE;
ENDLOOP;
};
colorBoardAddr: DoradoInputOutput.IOAddress ~ 360B;
colorDeviceAddr: DoradoInputOutput.IOAddress ~ 361B;
colorRevAddr: DoradoInputOutput.DMuxAddr ~ 3107B;
colorBoardData: WORD ← 0;
colorDeviceData: MACHINE DEPENDENT RECORD[type: [0..17B], junk: [0..7777B]] ← [0, 0];
colorRevData: DoradoInputOutput.MufManResult ← [0, 0];
Start
base0[csb] ← [mcb: RNIL];
colorBoardData ← DoradoInputOutput.InputNoPE[colorBoardAddr];
IF colorBoardData#0 THEN { -- Color display board is installed
colorDeviceData ← LOOPHOLE[DoradoInputOutput.InputNoPE[colorDeviceAddr]];
SELECT colorDeviceData.type FROM
17B => displayType ← standard;
16B => displayType ← highResolution;
ENDCASE; -- unknown display type
The following determines whether an old rev DispY board is installed.
colorRevData ← DoradoInputOutput.RWMufMan[[useDMD: FALSE, dMuxAddr: colorRevAddr]];
oldRev ← (colorRevData.dMuxData=1);
};
Margin offsets for various monitors...
AltoTerminal: 107B, Ramtek525: 66B, Ramtek1000: 0,
ConracRQB525: 66B, ConracRQB875: 107B, ConracRQB1225: 0,
END.