ColorDisplayHeadDoradoExtras.mesa
Last Edited by: Frank Crow, October 9, 1984 12:32:50 pm PDT
DIRECTORY
Basics       USING [BITSHIFT, bitsPerWord, LongMult, LowHalf, LongDivMod],
PrincOps      USING [PageNumber, PageCount, wordsPerPage],
VM       USING [Allocate, AddressForPageNumber, wordsPerPage, Interval,
           Pin, Free],
ColorDisplayFace    USING [Mode],
ColorDisplayFaceExtras  USING [DisplayType],
ColorDisplayHeadDorado;
ColorDisplayHeadDoradoExtras: PROGRAM
IMPORTS
Basics, ColorDisplayHeadDorado, VM
EXPORTS ColorDisplayFaceExtras
SHARES ColorDisplayHeadDorado =
BEGIN
OPEN ColorDisplayHeadDorado;
rpAChan2, rpBChan2: ChanCBPtr ← rpNIL;
achan2: LONG POINTER TO ChanCB ← NIL;
bchan2: LONG POINTER TO ChanCB ← NIL;
monitorType: ColorDisplayFaceExtras.DisplayType ← none;
monitorWidth, monitorHeight: NAT ← 0;
leftMarginBase: NAT ← 0;
widthA, widthB, heightA, heightB: NAT ← 0;
ColorDisplayHeadError: PUBLIC SIGNAL [reason: ATOM] ~ CODE;
Inlines copied from ColorDisplayHeadDorado
LPFromPage: PROC[page: PrincOps.PageNumber] RETURNS[LONG POINTER] = INLINE {
RETURN[LOOPHOLE[Basics.LongMult[page, PrincOps.wordsPerPage]]] };
WordsForPages: PROC[pages: CARDINAL] RETURNS[LONG CARDINAL] = INLINE {
RETURN[Basics.LongMult[pages,PrincOps.wordsPerPage]] };
PagesForWords: PROC[words: LONG CARDINAL] RETURNS[CARDINAL] = INLINE {
q,r: CARDINAL; [q,r] ← Basics.LongDivMod[words,PrincOps.wordsPerPage];
RETURN[IF r>0 THEN q+1 ELSE q] };
Exp: PROC[lg: Lg] RETURNS[CARDINAL] = INLINE { RETURN[Basics.BITSHIFT[1,lg]] }; -- 2^lg
PagesForMode: PUBLIC PROC[mode: ColorDisplayFace.Mode] RETURNS[PrincOps.PageCount] = {
a,b: CARDINAL ← 0; -- bits per pixel
words: LONG CARDINAL ← 0;
wordsPerLineA, wordsPerLineB: NAT ← 0;
IF NOT HasMode[mode] THEN RETURN[0];
words ← SIZE[ATableImage]; -- atable always required
IF mode.full THEN {
a ← 16; b ← 8; -- bitmap A has double pixels
words ← words + 2*SIZE[BCTableImage]; -- need 2 more color tables
}
ELSE {
IF mode.useA THEN a ← Exp[mode.lgBitsPerPixelA]; -- bitmap A
IF mode.useB THEN b ← Exp[mode.lgBitsPerPixelB]; -- bitmap B
};
wordsPerLineA ← (a*widthA)/Basics.bitsPerWord;
IF ((a*widthA) MOD Basics.bitsPerWord) # 0 THEN wordsPerLineA ← wordsPerLineA + 1;
wordsPerLineB ← (b*widthB)/Basics.bitsPerWord;
IF ((b*widthB) MOD Basics.bitsPerWord) # 0 THEN wordsPerLineB ← wordsPerLineB + 1;
words ← words + Basics.LongMult[ wordsPerLineA, heightA ]
     + Basics.LongMult[ wordsPerLineB, heightB ];
RETURN[PagesForWords[words]];
};
Set color display size and monitor type
SetMonitorType: PUBLIC PROC [
newMonitorType: ColorDisplayFaceExtras.DisplayType,
newWidth, newHeight: NAT
] ~ {
monitorWidth ← screenwidth ← width ← widthA ← widthB ← newWidth;
monitorHeight ← screenheight ← height ← heightA ← heightB ← newHeight;
monitorType ← newMonitorType;
};
Set color display window size
SetDisplaySize: PUBLIC PROC [ newWidthA, newHeightA: NAT,
          newWidthB, newHeightB: NAT ← 0 ] ~ {
screenwidth ← width ← MAX[newWidthA, newWidthB];
screenheight ← height ← MAX[newHeightA, newHeightB];
widthA ← newWidthA; heightA ← newHeightA;
widthB ← newWidthB; heightB ← newHeightB;
};
Set DCB fields to offset displayed position of A channel from upper left corner
SetAOffsets: PUBLIC PROC [ xOffset, yOffset: NAT ] ~ {
IF achan.link = rpNIL THEN ColorDisplayHeadError[$UnMoveableChannel];
IF xOffset > monitorWidth - widthA THEN xOffset ← monitorWidth - widthA;
achan.linesPerField ← yOffset/2; achan2.leftMargin ← leftMarginBase + xOffset;
};
Set DCB fields to offset displayed position of B channel from upper left corner
SetBOffsets: PUBLIC PROC [ xOffset, yOffset: NAT ] ~ {
IF bchan.link = rpNIL THEN ColorDisplayHeadError[$UnMoveableChannel];
IF xOffset > monitorWidth - widthB THEN xOffset ← monitorWidth - widthB;
bchan.linesPerField ← yOffset/2; bchan2.leftMargin ← leftMarginBase + xOffset;
};
Put A channel on B channel and vice versa
SwitchChannels: PUBLIC PROC [] ~ {
IF heightA # heightB OR widthA # widthB OR bppA # bppB
THEN ColorDisplayHeadError[$UnswitchableChannels];
IF mcb.achanCB = rpAChan THEN mcb.achanCB ← rpBChan ELSE mcb.achanCB ← rpAChan;
};
Establishes the specified mode; allocates bitmap(s) and colormap(s) from a client-supplied block of nPages pages of mapped virtual memory. If mode.full=TRUE, nPages may be less than PagesForMode[mode]; in this case, the raster size will be reduced to fit. Subsequent changes to the bitmap or color map will affect the color image, but the image will not appear on the screen until TurnOn is called.
Connect: PUBLIC PROC [
mode: ColorDisplayFace.Mode,
firstPage: PrincOps.PageNumber,
nPages: PrincOps.PageCount
] ~ {
allocBegin: LONG POINTER ← LPFromPage[firstPage]; -- beginning address
allocLimit: LONG CARDINAL = WordsForPages[nPages]; -- maximum words available
allocCount: LONG CARDINAL ← 0; -- words allocated so far
Alloc: PROC[words: LONG CARDINAL] RETURNS[LONG POINTER] = {
p: LONG POINTER ← allocBegin + allocCount; allocCount ← allocCount + words;
IF allocCount>allocLimit THEN ErrorHalt[]; RETURN[p]
};
AllocBitmap: PROC[bpl, height: CARDINAL] RETURNS [LONG POINTER] = {
wordsPerLine: NAT ← bpl/Basics.bitsPerWord;
IF (bpl MOD Basics.bitsPerWord) # 0 THEN wordsPerLine ← wordsPerLine + 1;
RETURN[Alloc[Basics.LongMult[wordsPerLine,height]]]
};
IF NOT initialized OR -- NOT HasMode[mode] OR -- connected THEN ErrorHalt[];
IF NOT SetSize[mode,nPages] THEN ErrorHalt[];
FreeDcbChain[];    -- clean up any stray DCB chain storage (should be in Disconnect)
SELECT monitorType FROM
ramtek714, hitachi2713 => {
color.vcontrol ← [VBtoVS: 0, VStoVS: 3, VStoVB: 16, VisibleLines: 240];
color.hcontrol ← [HRamMaxAddr: 378, HBLeadLength: 3, HSTrailAddr: 32, HBTrailLength: 23];
color.ccontrol ← [mul: 90, div: 12];
leftMarginBase ← 49;
pixelsPerInch ← 64;
};
conrac7211Lo => {
color.vcontrol ←
[VBtoVS: 0, VStoVS: 3, VStoVB: 16, VisibleLines: 240];
color.hcontrol ←
[HRamMaxAddr: 378, HBLeadLength: 3, HSTrailAddr: 32, HBTrailLength: 23];
color.ccontrol ← [mul: 90, div: 12];
leftMarginBase ← 54;
pixelsPerInch ← 42;
};
conrac7211Hi => {
color.vcontrol ← [VBtoVS: 0, VStoVS: 3, VStoVB: 18, VisibleLines: 384];
color.hcontrol ← [HRamMaxAddr: 601, HBLeadLength: 3, HSTrailAddr: 50, HBTrailLength: 35];
color.ccontrol ← [mul: 54, div: 14];
leftMarginBase ← 115;     -- might be 109?
pixelsPerInch ← 68;
};
ENDCASE => ErrorHalt[];
Allocate bitmaps first; they must begin at even addresses
Full-color Mode
IF mode.full THEN {
SELECT monitorType FROM
ramtek714, hitachi2713, conrac7211Lo => {
color.ccontrol ← [mul: 90, div: 14]; -- double the pixel clock rate 
horizontal control is measured in pixel clock periods
color.hcontrol.HRamMaxAddr ← 2*color.hcontrol.HRamMaxAddr;
color.hcontrol.HBLeadLength ← 2*color.hcontrol.HBLeadLength;
color.hcontrol.HSTrailAddr ← 2*color.hcontrol.HSTrailAddr;
color.hcontrol.HBTrailLength ← 2*color.hcontrol.HBTrailLength;
};
conrac7211Hi => {
IF height > 384 OR width > 512
THEN ERROR ColorDisplayHeadError[$ExcessHeightOrWidth];
pixelsPerInch ← 34;
};
ENDCASE => ErrorHalt[];
tchan.scan ← [mode24: TRUE, aChannelOnly: FALSE, bBypass: TRUE, a8b2: TRUE];
baseA ← AllocBitmap[bplA ← 2*8*width, heightA]; -- double pixels in bitmap A
baseB ← AllocBitmap[bplB ← 8*width, heightB];
bppA ← bppB ← 8; showA ← showB ← TRUE;
color.aTable ← atable ← Alloc[SIZE[ATableImage]];
color.bTable ← btable ← Alloc[SIZE[BCTableImage]];
color.cTable ← ctable ← Alloc[SIZE[BCTableImage]];
FOR i: NAT IN ATableIndex DO atable[i] ← [  -- ensure that zeroL and zeroH fields
redH: color.aTable[i].redH,        -- are defaulted to zero
redL: color.aTable[i].redL,
blue: color.aTable[i].blue,
green: color.aTable[i].green
];
ENDLOOP;
FOR i: NAT IN BCTableIndex DO btable[i] ← [value: color.bTable[i].value]; ENDLOOP;
FOR i: NAT IN BCTableIndex DO ctable[i] ← [value: color.cTable[i].value]; ENDLOOP;
fullmode ← TRUE;
}
Pseudo—color Mode
ELSE {
tchan.scan ← [
mode24: FALSE, aChannelOnly: ~mode.useB, bBypass: FALSE, a8b2: ~mode.lgBitsPerPixelB=2
];
IF mode.lgBitsPerPixelA > 0 THEN {
bppA ← Exp[mode.lgBitsPerPixelA];
bplA ← width*bppA;
baseA ← AllocBitmap[bplA, heightA];
IF mode.useA THEN showA ← TRUE
};
IF mode.lgBitsPerPixelB > 0 THEN {
bppB ← Exp[mode.lgBitsPerPixelB];
bplB ← width*bppB;
baseB ← AllocBitmap[bplB, heightB];
IF mode.useB AND (bppB = 2 OR mode.full) THEN showB ← TRUE;
};
color.aTable ← atable ← Alloc[SIZE[ATableImage]];
FOR i: NAT IN ATableIndex DO atable[i] ← [  -- ensure that zeroL and zeroH fields
redH: color.aTable[i].redH,        -- are defaulted to zero
redL: color.aTable[i].redL,
blue: color.aTable[i].blue,
green: color.aTable[i].green
];
ENDLOOP;
mapmode ← TRUE };
set up for MapIndex
IF tchan.scan.a8b2 THEN { ashift ← 10-bppA; amask ← 1774B; bshift ← 2-bppB; bmask ← 0003B }
ELSE { -- a6b4 -- ashift ← 10-bppA; amask ← 1760B; bshift ← 4-bppB; bmask ← 0017B };
set up channel control blocks; use tchan for a template
Set up top spacer to center undersized bitmap
IF bppA>0 AND heightA/2 < color.vcontrol.VisibleLines
THEN {
space: NAT ← (color.vcontrol.VisibleLines - heightA/2) / 2;
achan^ ← [ wordsPerLine: 0, linesPerField: space, pixelsPerLine: 0,
    leftMargin: leftMarginBase, scan: tchan.scan ];
achan2 ← @first64K[rpAChan2 ← RPAlloc[SIZE[ChanCB]]]; -- get pointer for another DCB
}
ELSE achan2 ← achan;
IF bppB>0 AND heightB/2 < color.vcontrol.VisibleLines THEN {
space: NAT ← (color.vcontrol.VisibleLines - heightB/2) / 2;
bchan^ ← [ wordsPerLine: 0, linesPerField: space, pixelsPerLine: 0,
    leftMargin: leftMarginBase, scan: tchan.scan ];
bchan2 ← @first64K[rpBChan2 ← RPAlloc[SIZE[ChanCB]]];
}
ELSE bchan2 ← bchan;
IF bppA>0 THEN { achan2^ ← tchan^; achan2.bitmap ← baseA;
achan2.leftMargin ← leftMarginBase + (monitorWidth - widthA) / 2;
achan2.pixelsPerLine ← widthA+pplOffset; achan2.linesPerField ← heightA/2;
achan2.wordsPerLine ← bplA/Basics.bitsPerWord;
IF (bplA MOD Basics.bitsPerWord) # 0 THEN achan2.wordsPerLine ← achan2.wordsPerLine +1;
achan2.scan.size ← bppA };
IF bppB>0 THEN { bchan2^ ← tchan^; bchan2.bitmap ← baseB;
bchan2.leftMargin ← leftMarginBase + (monitorWidth - widthB) / 2;
bchan2.pixelsPerLine ← widthB+pplOffset; bchan2.linesPerField ← heightB/2;
bchan2.wordsPerLine ← bplB/Basics.bitsPerWord;
IF (bplB MOD Basics.bitsPerWord) # 0 THEN bchan2.wordsPerLine ← bchan2.wordsPerLine +1;
bchan2.scan.size ← bppB };
IF achan2 # achan THEN achan.link ← rpAChan2;  -- set up pointers to centered DCBs
IF bchan2 # bchan THEN bchan.link ← rpBChan2;
tchan.pixelsPerLine ← 0 + pplOffset; -- now make tchan a dummy control block
IF fullmode THEN {        -- achan fetches two bitmap pixels per screen pixel
achan2.pixelsPerLine ← bchan2.pixelsPerLine ← 2*width+pplOffset;
bchan2.scan.res ← half;     --make B channel run at half speed for full color
IF monitorType = conrac7211Hi THEN { -- zoomed image on hi-res (1024x768) monitor
achan2.leftMargin ← leftMarginBase + (monitorWidth/2 - widthA);
bchan2.leftMargin ← leftMarginBase + (monitorWidth/2 - widthB);
achan.linesPerField ← (color.vcontrol.VisibleLines - heightA) / 2; -- go fix offset DCB
bchan.linesPerField ← (color.vcontrol.VisibleLines - heightB) / 2;
BuildZoomedDCBList[achan2]; 
BuildZoomedDCBList[bchan2];    -- set up DCBs for replicating scanlines
}
ELSE {       -- adjust leftMargin to compensate for the faster pixel clock
achan2.leftMargin ← 2*achan2.leftMargin + 40B--HWindow-- + 40B--marginCounter--;
bchan2.leftMargin ← 2*bchan2.leftMargin + 40B + 40B;
};
};
connected ← TRUE;
Disallow dangling pixels when using zoom (Causes atable register to not get cleared)
IF achan2.scan.res = half THEN achan2.pixelsPerLine ← 2 * (achan2.pixelsPerLine / 2);
IF achan2.scan.res = quarter THEN achan2.pixelsPerLine ← 4 * (achan2.pixelsPerLine / 4);
};
BuildZoomedDCBList: PUBLIC PROC [lastDcbPtr: LONG POINTER TO ChanCB] = {
Does vertical zoom by factor of 2. Produces a 2-line DCB for each scan line.
Larger Zooms will require a change in Dorado microcode
dcbRPtr: RPtr; dcbPtr: LONG POINTER TO ChanCB;
loopEnd: NAT ← lastDcbPtr.linesPerField * 2;
lastDcbPtr.linesPerField ← 1;
FOR i: NAT IN [1..loopEnd) DO
dcbPtr ← @first64K[dcbRPtr ← RPAlloc[SIZE[ChanCB]]];  -- get pointer for new DCB
dcbPtr^ ← lastDcbPtr^;           -- copy last chain entry
dcbPtr.bitmap ← dcbPtr.bitmap + dcbPtr.wordsPerLine;  -- move bitmap by one scan
lastDcbPtr.link ← dcbRPtr; lastDcbPtr ← dcbPtr;   -- link into chain
ENDLOOP;
};
zoomList: LIST OF VM.Interval ← NIL;
allocRPtr, limitRPtr: RPtr ← rpNIL;    -- pointers to display control block storage
RPAlloc: PROC[words: CARDINAL] RETURNS[pointer: RPtr] = {
interval: VM.Interval;
storageTop: RPtr ← allocRPtr + words;
IF (allocRPtr = rpNIL)        -- is there room in a currently allocated page?
OR (LOOPHOLE[storageTop, CARDINAL] > LOOPHOLE[limitRPtr, CARDINAL])
THEN {          -- allocate a page, pin it and list it for later recovery
interval ← VM.Allocate[count: 1, partition: lowCore, in64K: TRUE];
VM.Pin[ interval];
zoomList ← CONS[interval, zoomList];
allocRPtr ← LOOPHOLE[ Basics.LowHalf[
LOOPHOLE[VM.AddressForPageNumber[interval.page]]
]];
limitRPtr ← allocRPtr + VM.wordsPerPage;
};
pointer ← allocRPtr;
allocRPtr ← allocRPtr + words;
};
FreeDcbChain: PROC[] ~ {
WHILE zoomList # NIL DO
VM.Free[ zoomList.first ];
zoomList ← zoomList.rest;
ENDLOOP;
allocRPtr ← limitRPtr ← rpNIL;
};
END.