<> <> 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; <<>> <> 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; <> 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]]; }; <<>> <> SetMonitorType: PUBLIC PROC [ newMonitorType: ColorDisplayFaceExtras.DisplayType, newWidth, newHeight: NAT ] ~ { monitorWidth _ screenwidth _ width _ widthA _ widthB _ newWidth; monitorHeight _ screenheight _ height _ heightA _ heightB _ newHeight; monitorType _ newMonitorType; }; <> 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; }; <> 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; }; <> 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; }; <> 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; }; <<>> <> 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[]; <> 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[]; <> <> IF mode.full THEN { SELECT monitorType FROM ramtek714, hitachi2713, conrac7211Lo => { color.ccontrol _ [mul: 90, div: 14]; -- double the pixel clock rate <> 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; } <> 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 }; <> 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 }; <> <> 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; <> 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] = { <> <> 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.