-- MMDisplay.mesa; edited by Sandman; June 20, 1979 2:22 PM DIRECTORY AltoDefs: FROM "altodefs" USING [PageSize, wordlength], AltoDisplay: FROM "AltoDisplay" USING [ DCB, DCBchainHead, DCBHandle, DCBnil], BitBltDefs: FROM "bitbltdefs" USING [BBptr, BBTable, BITBLT], DisplayDefs: FROM "displaydefs" USING [Background], FontDefs: FROM "fontdefs" USING [BitmapState, FontHandle], FrameDefs: FROM "framedefs" USING [GlobalFrame, SwapInCode], MMInit: FROM "MMInit", MMOps: FROM "MMOps" USING [MMFont], ImageDefs: FROM "imagedefs" USING [ AddCleanupProcedure, CleanupItem, CleanupMask, CleanupProcedure], InlineDefs: FROM "inlinedefs" USING [BITAND, BITOR, BITSHIFT, BITXOR, COPY], ProcessDefs: FROM "processdefs" USING [MsecToTicks, SetTimeout], SegmentDefs: FROM "segmentdefs" USING [ DataSegmentAddress, DataSegmentHandle, DefaultBase, DeleteDataSegment, NewDataSegment], StreamDefs: FROM "streamdefs" USING [ DisplayHandle, StreamError, StreamHandle, StreamObject]; MMDisplay: MONITOR IMPORTS BitBltDefs, FrameDefs, ImageDefs, InlineDefs, ProcessDefs, SegmentDefs, StreamDefs, MMOps EXPORTS DisplayDefs, MMInit, StreamDefs SHARES StreamDefs = BEGIN OPEN AltoDisplay; BitsPerWord: CARDINAL = AltoDefs.wordlength; BitmapState: TYPE = FontDefs.BitmapState; StreamHandle: TYPE = StreamDefs.StreamHandle; OrderedPOINTER: TYPE = ORDERED POINTER; OrderedNIL: OrderedPOINTER = LOOPHOLE[NIL]; TAB: CHARACTER = 11C; CR: CHARACTER = 15C; NUL: CHARACTER = 0C; SP: CHARACTER = ' ; LeftIndent: CARDINAL; LeftMargin: CARDINAL; RightMargin: CARDINAL; WordsPerLine: CARDINAL; MaxWordsPerLine: CARDINAL = 38; DSP: Display StreamDefs.StreamObject ← StreamDefs.StreamObject [ reset: ClearDS, get: GetNop, put: DPutChar, putback: PutbackNop, endof: EndofNop, destroy: DestroyNop, link: NIL, body: Display[clearCurrentLine: ClearCurrentLine, clearLine: CLNop, clearChar: ClearDisplayChar, type: 0, data: NIL]]; systemDS: StreamDefs.DisplayHandle = @DSP; displayOn: BOOLEAN ← FALSE; lineheight: CARDINAL; TABindex: CARDINAL; TABs: ARRAY [0..10) OF CARDINAL; bmSegment: SegmentDefs.DataSegmentHandle; bmFirst, bmTail, bmNext, bmLastLine: OrderedPOINTER; lastLineSize: CARDINAL ← 0; dummyDCB: DCBHandle; firstDCB, lastDCB, currentDCB: DCBHandle ← DCBnil; -- layout of bitmap is: -- DCBs: ARRAY [firstDCB..lastDCB] OF DCB, -- bmFirst: ARRAY OF UNSPECIFIED, -- bmLastLine: ARRAY [0..lineheight*WordsPerLine) OF UNSPECIFIED, -- bmNext points to next word to allocate, -- bmTail points to oldest allocated bitmap. bmState: FontDefs.BitmapState; tabWidth: CARDINAL; -- Typescript data GetDefaultDisplayStream: PUBLIC PROCEDURE RETURNS[StreamDefs.DisplayHandle] = BEGIN RETURN[systemDS]; END; NotEnoughSpaceForDisplay: PUBLIC SIGNAL = CODE; SetupBitmap: PROCEDURE [bitmap: POINTER, nLines, nWords: CARDINAL] = BEGIN dcb: DCBHandle; WHILE nLines*SIZE[DCB] + lastLineSize > nWords DO IF nLines > 1 THEN nLines ← nLines - 1 ELSE BEGIN IF WordsPerLine = 2 THEN ERROR NotEnoughSpaceForDisplay; WordsPerLine ← WordsPerLine - 2; RightMargin ← WordsPerLine * BitsPerWord; lastLineSize ← lineheight*WordsPerLine; END; ENDLOOP; currentLines ← nLines; firstDCB ← dcb ← bitmap; THROUGH [0..nLines) DO dcb.next ← dcb + SIZE[DCB]; dcb.height ← lineheight/2; dcb ← dcb.next; ENDLOOP; lastDCB ← dcb-SIZE[DCB]; lastDCB.next ← DCBnil; bmFirst ← LOOPHOLE[dcb, OrderedPOINTER]; bmLastLine ← LOOPHOLE[bitmap+nWords-lastLineSize, OrderedPOINTER]; bmState ← [origin: bmLastLine, wordsPerLine: WordsPerLine, x:, y:0]; END; currentPages, currentLines, currentDummySize: CARDINAL; DisplayOff: PUBLIC PROCEDURE [color: DisplayDefs.Background] = BEGIN IF ~displayOn THEN RETURN; SetSystemDisplaySize[0,0]; dummyDCB.background ← color; dummyDCB.height ← 1; END; DisplayOn: PUBLIC PROCEDURE = BEGIN IF displayOn THEN RETURN; dummyDCB.background ← white; SetDummyDisplaySize[currentDummySize]; SetSystemDisplaySize[currentLines, currentPages]; END; SetSystemDisplaySize: PUBLIC PROCEDURE [nTextLines, nPages: CARDINAL] = BEGIN OPEN SegmentDefs; IF displayOn THEN BEGIN firstDCB.next ← lastDCB.next; RemoveDCB[firstDCB]; firstDCB ← lastDCB ← currentDCB ← DCBnil; DeleteDataSegment[bmSegment]; blinkOn ← displayOn ← FALSE; END; IF nPages = 0 THEN RETURN; currentPages ← nPages; -- for Display Off/On currentLines ← nTextLines; -- for Display Off/On bmSegment ← NewDataSegment[DefaultBase, nPages]; SetupBitmap[DataSegmentAddress[bmSegment], nTextLines, nPages*AltoDefs.PageSize]; displayOn ← TRUE; ClearDS[systemDS]; InsertDCB[new: firstDCB, before: dummyDCB.next]; RETURN END; SetSystemDisplayWidth: PUBLIC PROCEDURE [indent, width: CARDINAL] = BEGIN max: CARDINAL; LeftMargin ← indent MOD BitsPerWord; LeftIndent ← indent / BitsPerWord; max ← Even[MaxWordsPerLine-1-LeftIndent]; WordsPerLine ← MIN[Even[width/BitsPerWord],max]; RightMargin ← WordsPerLine * BitsPerWord; IF displayOn THEN SetSystemDisplaySize[currentLines, currentPages]; RETURN END; SetDummyDisplaySize: PUBLIC PROCEDURE [nScanLines: CARDINAL] = BEGIN currentDummySize ← nScanLines; -- for Display Off/On IF nScanLines/2 = dummyDCB.height THEN RETURN; IF dummyDCB.height # 0 THEN RemoveDCB[dummyDCB]; dummyDCB.height ← nScanLines/2; IF dummyDCB.height # 0 THEN InsertDCB[new: dummyDCB, before: firstDCB]; RETURN END; ClearDS: PROCEDURE [stream: StreamHandle] = BEGIN dcb: DCBHandle; IF stream # systemDS THEN SIGNAL StreamDefs.StreamError[stream,StreamType]; IF ~displayOn THEN RETURN; FOR dcb ← firstDCB, dcb.next DO dcb.resolution ← high; dcb.background ← white; dcb.indenting ← dcb.width ← 0; dcb.bitmap ← bmLastLine; IF dcb = lastDCB THEN EXIT; ENDLOOP; bmNext ← bmFirst; bmTail ← bmLastLine; currentDCB ← firstDCB; ClearCurrentLine[stream]; RETURN END; ClearCurrentLine: PUBLIC PROCEDURE [stream: StreamHandle] = BEGIN IF stream # systemDS THEN SIGNAL StreamDefs.StreamError[stream,StreamType]; IF ~displayOn THEN RETURN; bmLastLine↑ ← 0; InlineDefs.COPY[from: bmLastLine, to: bmLastLine+1, nwords: lastLineSize-1]; currentDCB.indenting ← LeftIndent; currentDCB.bitmap ← bmLastLine; currentDCB.width ← WordsPerLine; bmState.x ← LeftMargin; blinkOn ← FALSE; TABindex ← 0; RETURN END; CLNop: PUBLIC PROCEDURE [stream: StreamHandle, line: CARDINAL] = BEGIN END; Scroll: PROCEDURE [char: CHARACTER] = BEGIN OPEN BitBltDefs; bbt: ARRAY [0..SIZE[BBTable]] OF WORD; bbp: BBptr ← Even[BASE[bbt]]; pos: CARDINAL; SELECT char FROM CR => NULL; TAB => BEGIN TABs[TABindex] ← bmState.x; TABindex ← TABindex + 1; pos ← (LOOPHOLE[bmState.x-LeftMargin,CARDINAL]/tabWidth+1) * tabWidth+LeftMargin; IF pos < RightMargin THEN bmState.x ← pos ELSE DPutChar[systemDS, SP]; RETURN END; NUL => RETURN; ENDCASE => IF char < 40C THEN BEGIN DPutChar[systemDS, '↑]; DPutChar[systemDS, LOOPHOLE[LOOPHOLE[char,CARDINAL]+100B,CHARACTER]]; RETURN END; -- Do the scroll, assuming last (current) line is in bmLastLine. -- scroll all others by BLTing their DCBs. move old last line to -- new bitmap and free bmLastLine for reuse. UNTIL Compact[currentDCB,bmState.x] DO IF ~DeleteTopLine[] THEN RETURN;-- not enough space ENDLOOP; IF currentDCB # lastDCB THEN currentDCB ← currentDCB.next ELSE BEGIN IF firstDCB.width # 0 THEN [] ← DeleteTopLine[]; bbp↑ ← [ pad: 0, sourcealt: FALSE, destalt: FALSE, sourcetype: block, function: replace, dbca: firstDCB, dbmr: SIZE[DCB], dlx: 16, dty: 0, dw: 16*(SIZE[DCB]-1), dh: currentLines-1, sbca: firstDCB, sbmr: SIZE[DCB], slx: 16, sty: 1, unused:, gray0:, gray1:, gray2:, gray3:]; IF firstDCB # lastDCB THEN BITBLT[bbp]; END; ClearCurrentLine[systemDS]; IF char # CR THEN DPutChar[systemDS, char]; END; DeleteTopLine: PROCEDURE RETURNS [BOOLEAN] = BEGIN dcb: DCBHandle; -- find first line with bitmap allocated FOR dcb ← firstDCB, dcb.next DO IF dcb.width # 0 THEN EXIT; IF dcb = lastDCB THEN RETURN[FALSE]; -- found no top line to delete ENDLOOP; dcb.width ← dcb.indenting ← 0; -- find next line with bitmap allocated UNTIL dcb = lastDCB DO dcb ← dcb.next; IF dcb.width # 0 THEN BEGIN bmTail ← LOOPHOLE[dcb.bitmap]; EXIT END; REPEAT FINISHED => -- all linex deleted BEGIN bmTail ← bmLastLine; bmNext ← bmFirst END; ENDLOOP; RETURN[TRUE]; END; Compact: PROCEDURE [dcb: DCBHandle, x: CARDINAL] RETURNS [BOOLEAN] = BEGIN newWidth: CARDINAL ← (x+15)/16; oldWidth: CARDINAL ← dcb.width; old: OrderedPOINTER ← LOOPHOLE[dcb.bitmap]; lineHeight: CARDINAL ← dcb.height*2; d: CARDINAL; IF x <= LeftMargin THEN d ← 0 ELSE FOR d IN [0..newWidth) DO BEGIN p: OrderedPOINTER; p ← old + d; THROUGH [0..lineHeight) DO IF p↑ # 0 THEN GO TO foundit; p ← p + oldWidth; ENDLOOP; END; REPEAT foundit => NULL; ENDLOOP; newWidth ← Even[newWidth-d]; IF newWidth > 0 THEN BEGIN OPEN BitBltDefs; bbt: ARRAY [0..SIZE[BBTable]] OF WORD; bbp: BBptr ← Even[BASE[bbt]]; new: OrderedPOINTER; IF (new ← GetMapSpace[newWidth*lineHeight]) = OrderedNIL THEN RETURN[FALSE]; dcb.width ← 0; bbp↑ ← [ pad: 0, sourcealt: FALSE, destalt: FALSE, sourcetype: block, function: replace, dbca: new, dbmr: newWidth, dlx: 0, dty: 0, dw: newWidth*16, dh: lineHeight, sbca: old, sbmr: oldWidth, slx: d*16, sty: 0, unused:, gray0:, gray1:, gray2:, gray3:]; BITBLT[bbp]; dcb.indenting ← LeftIndent + d; dcb.bitmap ← new; dcb.width ← newWidth; END ELSE dcb.indenting ← dcb.width ← 0; RETURN [ TRUE ]; END; GetMapSpace: PROCEDURE [nwords: [0..77777B]] RETURNS [p: OrderedPOINTER] = BEGIN t: INTEGER; DO t ← bmTail - bmNext; IF t < 0 THEN IF bmLastLine >= bmNext+nwords THEN EXIT ELSE bmNext ← bmFirst ELSE IF t >= nwords THEN EXIT ELSE RETURN[OrderedNIL]; ENDLOOP; p ← bmNext; bmNext ← bmNext + nwords; RETURN END; DPutChar: PROCEDURE [stream: StreamHandle, char: UNSPECIFIED] = BEGIN IF stream # systemDS THEN SIGNAL StreamDefs.StreamError[stream,StreamType]; IF ~displayOn THEN RETURN; IF char > 377B THEN RETURN; IF blinkOn THEN [] ← BlinkCursor[]; IF char < 40B OR CharWidth[char] + bmState.x > RightMargin THEN Scroll[char] ELSE PaintChar[char,@bmState]; RETURN END; ClearDisplayChar: PUBLIC PROCEDURE [stream: StreamHandle, char: UNSPECIFIED] = BEGIN IF stream # systemDS THEN SIGNAL StreamDefs.StreamError[stream,StreamType]; IF displayOn THEN BEGIN IF blinkOn THEN [] ← BlinkCursor[]; SELECT char FROM NUL, CR, > 377B => RETURN; TAB => BEGIN IF TABindex > 0 THEN BEGIN TABindex ← TABindex -1; bmState.x ← TABs[TABindex]; END; RETURN END; < 40B => BEGIN ClearDisplayChar[stream, char+100B]; char ← '↑; END; ENDCASE => NULL; ClearChar[char,@bmState]; END; RETURN END; GetNop: PROCEDURE [stream: StreamHandle] RETURNS [UNSPECIFIED] = BEGIN ERROR StreamDefs.StreamError[stream,StreamAccess] END; PutbackNop: PROCEDURE [stream: StreamHandle, char: UNSPECIFIED] = BEGIN ERROR StreamDefs.StreamError[stream,StreamAccess] END; EndofNop: PROCEDURE [stream: StreamHandle] RETURNS [BOOLEAN] = BEGIN IF stream # systemDS THEN SIGNAL StreamDefs.StreamError[stream,StreamType]; RETURN[FALSE] END; DestroyNop: PROCEDURE [stream: StreamHandle] = BEGIN ERROR StreamDefs.StreamError[stream,StreamAccess] END; ddarray: ARRAY [0..SIZE[DCB]] OF UNSPECIFIED; InsertDCB: PROCEDURE [new: DCBHandle, before: DCBHandle] = BEGIN dcb: DCBHandle; FOR dcb ← new, dcb.next DO IF dcb.next = DCBnil THEN BEGIN dcb.next ← before; EXIT END; ENDLOOP; IF DCBchainHead↑ = before THEN DCBchainHead↑ ← new ELSE FOR dcb ← DCBchainHead↑, dcb.next DO IF dcb.next = before THEN BEGIN dcb.next ← new; EXIT END; ENDLOOP; END; RemoveDCB: PROCEDURE [dcb: DCBHandle] = BEGIN prev: DCBHandle; IF DCBchainHead↑ = dcb THEN DCBchainHead↑ ← dcb.next ELSE FOR prev ← LOOPHOLE[DCBchainHead], prev.next UNTIL prev.next = DCBnil DO IF prev.next = dcb THEN BEGIN prev.next ← dcb.next; EXIT END; ENDLOOP; dcb.next ← DCBnil; END; Even: PROCEDURE [a: UNSPECIFIED] RETURNS [UNSPECIFIED] = BEGIN RETURN[a + CARDINAL[a] MOD 2] END; blinkOn: BOOLEAN ← FALSE; BlinkCursor: PUBLIC PROCEDURE RETURNS [BOOLEAN] = BEGIN OPEN InlineDefs; mask: WORD; p: POINTER; IF ~displayOn THEN RETURN[blinkOn]; mask ← BITSHIFT[3, 14 - CARDINAL[bmState.x+1] MOD 16]; p ← bmState.origin + (bmState.x+1)/16 + bmState.wordsPerLine; THROUGH [2..lineheight) DO p↑ ← BITXOR[p↑, mask]; IF mask = 1 THEN (p+1)↑ ← BITXOR[(p+1)↑, 100000B]; p ← p + bmState.wordsPerLine; ENDLOOP; RETURN[blinkOn ← ~blinkOn] END; InitDisplay: PUBLIC PROCEDURE [ dummySize, textLines, nPages: CARDINAL, f: FontDefs.FontHandle] = BEGIN lineheight ← Even[CharHeight['A]]; lastLineSize ← lineheight*WordsPerLine; tabWidth ← CharWidth[SP]*8; SetDummyDisplaySize[dummySize]; SetSystemDisplaySize[textLines, nPages]; RETURN END; CleanupDisplay: ImageDefs.CleanupItem ← [link:, proc: Cleanup, mask: ImageDefs.CleanupMask[Finish] + ImageDefs.CleanupMask[Abort]]; Cleanup: ImageDefs.CleanupProcedure = BEGIN SELECT why FROM Finish, Abort => DCBchainHead.next ← DCBnil; ENDCASE; END; font: Fptr; FHptr: TYPE = POINTER TO FontHeader; Fptr: TYPE = POINTER TO Font; FCDptr: TYPE = POINTER TO FCD; FAptr: TYPE = POINTER TO FontArray; FontArray: TYPE = ARRAY [0..255] OF FCDptr; Font: TYPE = MACHINE DEPENDENT RECORD [ header: FontHeader, FCDptrs: FontArray, -- array of self-relative pointers to -- FCD's. Indexed by char value. -- font pointer points hear! extFCDptrs: FontArray -- array of self-relative pointers to -- FCD's for extentions. As large an -- array as needed. ]; FontHeader: TYPE = MACHINE DEPENDENT RECORD [ maxHeight: CARDINAL, -- height of tallest char in font (scan lines) variableWidth: BOOLEAN, -- IF TRUE, proportionally spaced font blank: [0..177B], -- not used maxWidth: [0..377B] -- width of widest char in font (raster units). ]; FCD: TYPE = MACHINE DEPENDENT RECORD [ widthORext: [0..77777B], -- width or extention index hasNoExtension: BOOLEAN, -- TRUE=> no ext.;prevfield=width height: [0..377B], -- # scan lines to skip for char displacement: [0..377B] -- displacement back to char bitmap ]; CharWidth: PUBLIC PROCEDURE [char: CHARACTER] RETURNS [w: CARDINAL] = BEGIN code: CARDINAL; cw: FCDptr; fontdesc: FAptr; -- checkfor control characters IF char = CR THEN char ← SP; IF char < SP THEN RETURN[CharWidth['↑] + CharWidth[LOOPHOLE[LOOPHOLE[char,CARDINAL]+100B,CHARACTER]]]; w ← 0; fontdesc ← @font.FCDptrs; code ← LOOPHOLE[char]; DO cw ← LOOPHOLE[fontdesc[code]+LOOPHOLE[fontdesc,CARDINAL]+code]; IF cw.hasNoExtension THEN EXIT; w ← w+16; code ← cw.widthORext; ENDLOOP; w ← w+cw.widthORext; RETURN END; CharHeight: PUBLIC PROCEDURE [char: CHARACTER] RETURNS [CARDINAL] = BEGIN RETURN[font.header.maxHeight] END; PaintChar: PROCEDURE [char: CHARACTER, bmState: POINTER TO BitmapState] = BEGIN OPEN BitBltDefs, bmState; bba: ARRAY [0..SIZE[BBTable]] OF UNSPECIFIED; bbt: BBptr = LOOPHOLE[BASE[bba] + LOOPHOLE[BASE[bba],CARDINAL] MOD 2]; cw: FCDptr; fontdesc: FAptr = @font.FCDptrs; code: CARDINAL ← LOOPHOLE[char]; bbt↑ ← [ pad: 0, sourcealt: FALSE, destalt: FALSE, sourcetype: block, function: paint, unused:, dbca: origin, dbmr: wordsPerLine, dlx: x, dty:, dw: 16, dh:, sbca:, sbmr: 1, slx: 0, sty: 0, gray0:, gray1:, gray2:, gray3:]; DO cw ← LOOPHOLE[fontdesc[code]+LOOPHOLE[fontdesc,CARDINAL]+code]; bbt.dty ← y + cw.height; bbt.dh ← cw.displacement; bbt.sbca ← cw - (bbt.dh ← cw.displacement); IF cw.hasNoExtension THEN BEGIN x ← x + (bbt.dw ← cw.widthORext); BITBLT[bbt]; EXIT END ELSE BEGIN BITBLT[bbt]; bbt.dlx ← x ← x + 16; END; code ← cw.widthORext; ENDLOOP; RETURN END; ClearChar: PROCEDURE [char: CHARACTER, bmState: POINTER TO BitmapState] = BEGIN OPEN bmState, InlineDefs; bit: [0..15]; xword: CARDINAL; scanLines: CARDINAL = font.header.maxHeight; start,p: POINTER; cwidth: INTEGER ← CharWidth[char]; mask: WORD; ones: WORD = 177777B; IF x < cwidth THEN BEGIN cwidth ← x; x ← 0 END ELSE x ← x - cwidth; xword ← x/16; bit ← x MOD 16; mask ← BITOR[BITSHIFT[ones,16-bit],BITSHIFT[ones,-(bit+cwidth)]]; start ← origin + xword + y*wordsPerLine-1; cwidth ← cwidth + bit; DO p ← start ← start + 1; THROUGH [0..scanLines) DO p↑ ← BITAND[p↑,mask]; p ← p + wordsPerLine; ENDLOOP; IF (cwidth ← cwidth - 16) <= 0 THEN EXIT; mask ← BITSHIFT[ones,-cwidth]; ENDLOOP; RETURN END; Cursor: ENTRY PROCEDURE = BEGIN wait: CONDITION; timer: POINTER TO INTEGER ← LOOPHOLE[430B]; blinktime: INTEGER; ProcessDefs.SetTimeout[@wait, ProcessDefs.MsecToTicks[450]]; DO -- forever IF blinktime-timer↑ ~IN[0..13] THEN BEGIN [] ← BlinkCursor[]; blinktime ← timer↑+13 END; IF stopCursor THEN EXIT; WAIT wait; ENDLOOP; RETURN END; stopCursor: BOOLEAN ← TRUE; CursorProcess: PUBLIC PROCESS; StartCursor: PUBLIC PROCEDURE = BEGIN IF stopCursor THEN BEGIN stopCursor ← FALSE; CursorProcess ← FORK Cursor[]; END; RETURN END; StopCursor: PUBLIC PROCEDURE = BEGIN IF ~stopCursor THEN BEGIN stopCursor ← TRUE; JOIN CursorProcess END; RETURN END; Init: PROCEDURE = BEGIN FrameDefs.SwapInCode[LOOPHOLE[MMOps.MMFont]]; font ← FrameDefs.GlobalFrame[MMOps.MMFont].code.shortbase; ImageDefs.AddCleanupProcedure[@CleanupDisplay]; dummyDCB ← Even[BASE[ddarray]]; dummyDCB↑ ← [DCBnil,high,white,0,0,LOOPHOLE[0,OrderedPOINTER],0]; SetSystemDisplayWidth[16,36*16]; InitDisplay[24,14,20,NIL]; StartCursor[]; END; Init[]; END.