-- SystemDisplay.mesa; edited by Sandman June 4, 1980 3:27 PM -- Copyright Xerox Corporation 1979, 1980 DIRECTORY AltoDefs USING [PageSize, wordlength], AltoDisplay USING [Background, DCB, DCBchainHead, DCBHandle, DCBnil], BitBltDefs USING [AlignedBBTable, BBptr, BBTableSpace, BITBLT], DisplayDefs USING [], FontDefs USING [BitmapState, FontHandle], ForgotDefs USING [BitmapDS], FrameOps USING [MyGlobalFrame], InlineDefs USING [BITSHIFT, BITXOR, COPY], SegmentDefs USING [ DataSegmentHandle, DataSegmentAddress, DefaultBase, DeleteDataSegment, HardDown, MakeDataSegment], StreamDefs USING [ DiskHandle, DisplayHandle, GetPosition, SetPosition, StreamError, StreamHandle, StreamPosition, StreamObject], Storage USING [Node, Free]; SystemDisplay: PROGRAM IMPORTS BitBltDefs, FrameOps, InlineDefs, StreamDefs, SegmentDefs, Storage EXPORTS DisplayDefs, StreamDefs SHARES StreamDefs = BEGIN OPEN AltoDisplay; BitsPerWord: CARDINAL = AltoDefs.wordlength; StreamHandle: TYPE = StreamDefs.StreamHandle; OrderedPOINTER: TYPE = ORDERED POINTER; OrderedNIL: OrderedPOINTER = LOOPHOLE[NIL]; TAB: CHARACTER = 11C; CR: CHARACTER = 15C; NUL: CHARACTER = 0C; SP: CHARACTER = ' ; -- Display Hardware DisplayData: TYPE = RECORD [ firstDCB, lastDCB, currentDCB: DCBHandle, bmFirst, bmTail, bmNext, bmLastLine: OrderedPOINTER, LeftIndent, LeftMargin, RightMargin, WordsPerLine: CARDINAL, lineheight: CARDINAL, lastLineSize: CARDINAL, bmState: FontDefs.BitmapState, bmSeg: SegmentDefs.DataSegmentHandle, bitmap: POINTER, tabIndex: CARDINAL, tabs: ARRAY [0..10) OF CARDINAL, displayOn, blinkOn: BOOLEAN, dummyDCB: DCBHandle, tabWidth, currentPages, currentLines, currentDummySize: CARDINAL, startLine: StreamDefs.StreamPosition, ddarray: ARRAY [0..SIZE[DCB]] OF UNSPECIFIED, DSP: Display StreamDefs.StreamObject]; data: POINTER TO DisplayData ← NIL; systemDS: StreamDefs.DisplayHandle; font: FontDefs.FontHandle; typescript: StreamDefs.DiskHandle; CreateDisplayData: PUBLIC PROCEDURE = BEGIN data ← Storage.Node[SIZE[DisplayData]]; font ← NIL; data.firstDCB ← data.lastDCB ← data.currentDCB ← DCBnil; typescript ← NIL; data.startLine ← 0; data.displayOn ← data.blinkOn ← FALSE; data.DSP ← StreamDefs.StreamObject[ reset: ResetDS, get: GetNop, put: DPutChar, putback: PutbackNop, endof: EndofNop, destroy: DestroyNop, link: NIL, body: Display[ clearCurrentLine: ClearCurrentLine, clearChar: ClearChar, clearLine: ClearLine, type: FrameOps.MyGlobalFrame[], data: NIL]]; systemDS ← @data.DSP; data.dummyDCB ← Even[BASE[data.ddarray]]; data.dummyDCB↑ ← [DCBnil, high, white, 0, 0, LOOPHOLE[0, OrderedPOINTER], 0]; SetSystemDisplayWidth[16, 36*16]; RETURN END; DeleteDisplayData: PUBLIC PROCEDURE = {Storage.Free[data]}; -- layout of bitmap is: -- DCBs: ARRAY [firstDCB..lastDCB] OF DCB, -- bmFirst: ARRAY OF UNSPECIFIED, -- bmLastLine: ARRAY [0..DSP.lineheight*WordsPerLine) OF UNSPECIFIED, -- bmNext points to next word to allocate, -- bmTail points to oldest allocated bitmap. GetDefaultDisplayStream: PUBLIC PROCEDURE RETURNS [StreamDefs.DisplayHandle] = BEGIN RETURN[systemDS]; END; NotEnoughSpaceForDisplay: PUBLIC SIGNAL = CODE; SetupBitmap: PROCEDURE [bitmap: POINTER, nLines, nWords: CARDINAL] = BEGIN OPEN data; dcb: DCBHandle; lastLineSize ← lineheight*WordsPerLine; 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; DisplayOff: PUBLIC PROCEDURE [color: Background] = BEGIN OPEN data; IF ~displayOn THEN RETURN; SetSystemDisplaySize[0, 0]; font.close[font]; dummyDCB.background ← color; dummyDCB.height ← 1; END; DisplayOn: PUBLIC PROCEDURE = BEGIN OPEN data; IF displayOn THEN RETURN; dummyDCB.background ← white; SetDummyDisplaySize[currentDummySize]; SetSystemDisplaySize[currentLines, currentPages]; END; SetSystemDisplaySize: PUBLIC PROCEDURE [nTextLines, nPages: CARDINAL] = BEGIN OPEN SegmentDefs, data; IF displayOn THEN BEGIN firstDCB.next ← lastDCB.next; RemoveDCB[firstDCB]; firstDCB ← lastDCB ← currentDCB ← DCBnil; DeleteDataSegment[bmSeg]; blinkOn ← displayOn ← FALSE; END; IF nPages = 0 THEN RETURN; currentPages ← nPages; -- for Display Off/On currentLines ← nTextLines; -- for Display Off/On bitmap ← DataSegmentAddress[ bmSeg ← MakeDataSegment[DefaultBase, nPages, HardDown]]; bmSeg.type ← ForgotDefs.BitmapDS; SetupBitmap[bitmap, nTextLines, nPages*AltoDefs.PageSize]; displayOn ← TRUE; ClearDS[systemDS]; InsertDCB[new: firstDCB, before: dummyDCB.next]; RETURN END; SetSystemDisplayWidth: PUBLIC PROCEDURE [indent, width: CARDINAL] = BEGIN OPEN data; LeftMargin ← indent MOD BitsPerWord; LeftIndent ← indent/BitsPerWord; WordsPerLine ← Even[width/BitsPerWord]; RightMargin ← WordsPerLine*BitsPerWord; IF displayOn THEN SetSystemDisplaySize[currentLines, currentPages]; RETURN END; SetDummyDisplaySize: PUBLIC PROCEDURE [nScanLines: CARDINAL] = BEGIN OPEN data; 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; ResetDS: PROCEDURE [stream: StreamHandle] = BEGIN ClearDS[stream]; IF typescript # NIL THEN typescript.reset[typescript]; data.startLine ← 0; RETURN END; ClearDS: PROCEDURE [stream: StreamHandle] = BEGIN OPEN data; dcb: DCBHandle; 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 OPEN data; IF typescript # NIL THEN StreamDefs.SetPosition[typescript, startLine]; 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; ClearLine: PROCEDURE [stream: StreamHandle, line: CARDINAL] = BEGIN END; Scroll: PROCEDURE [char: CHARACTER] = BEGIN OPEN BitBltDefs, data; bbt: BBTableSpace; bbp: BBptr ← AlignedBBTable[@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↑ ← [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]; IF firstDCB # lastDCB THEN BITBLT[bbp]; END; IF typescript # NIL THEN startLine ← StreamDefs.GetPosition[typescript]; ClearCurrentLine[systemDS]; IF char # CR THEN DPutChar[systemDS, char]; END; DeleteTopLine: PROCEDURE RETURNS [BOOLEAN] = BEGIN OPEN data; 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 OPEN data; 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: BBTableSpace; bbp: BBptr ← AlignedBBTable[@bbt]; new: OrderedPOINTER; IF (new ← GetMapSpace[newWidth*lineHeight]) = OrderedNIL THEN RETURN[FALSE]; dcb.width ← 0; bbp↑ ← [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]; 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 OPEN data; DO IF bmTail < bmNext THEN IF bmLastLine >= bmNext + nwords THEN EXIT ELSE bmNext ← bmFirst ELSE IF bmTail - bmNext >= nwords THEN EXIT ELSE RETURN[OrderedNIL]; ENDLOOP; p ← bmNext; bmNext ← bmNext + nwords; RETURN END; DPutChar: PROCEDURE [stream: StreamHandle, char: UNSPECIFIED] = BEGIN OPEN data; IF ~displayOn THEN RETURN; IF char > 377B THEN RETURN; IF blinkOn THEN [] ← BlinkCursor[]; IF char < 40B OR font.charWidth[font, char] + bmState.x > RightMargin THEN Scroll[char] ELSE font.paintChar[font, char, @bmState]; RETURN END; DPutCharTS: PROCEDURE [stream: StreamHandle, char: UNSPECIFIED] = BEGIN typescript.put[typescript, char]; DPutChar[stream, char]; IF ~data.displayOn AND char = CR THEN data.startLine ← StreamDefs.GetPosition[typescript]; RETURN END; ClearChar: PROCEDURE [stream: StreamHandle, char: UNSPECIFIED] = BEGIN OPEN data; 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; font.clearChar[font, char, @bmState]; END ELSE SELECT char FROM NUL, CR, TAB, > 377B => RETURN; < 40B => ClearDisplayChar[stream, char + 100B]; ENDCASE => NULL; RETURN END; ClearDisplayChar: PUBLIC PROCEDURE [stream: StreamHandle, char: UNSPECIFIED] = BEGIN ClearChar[stream, char]; IF typescript # NIL THEN BEGIN OPEN StreamDefs; SetPosition[typescript, GetPosition[typescript] - 1]; 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 RETURN[FALSE] END; DestroyNop: PROCEDURE [stream: StreamHandle] = BEGIN ERROR StreamDefs.StreamError[stream, StreamAccess] END; InsertDCB: PROCEDURE [new: DCBHandle, before: DCBHandle] = BEGIN OPEN data; 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; SetFont: PUBLIC PROCEDURE [f: FontDefs.FontHandle] = BEGIN OPEN data; font ← f; lineheight ← Even[font.charHeight[font, 'A]]; tabWidth ← font.charWidth[font, SP]*8; RETURN END; GetFont: PUBLIC PROCEDURE RETURNS [FontDefs.FontHandle] = BEGIN RETURN[font] END; SetTypeScript: PUBLIC PROCEDURE [ts: StreamDefs.DiskHandle] = BEGIN OPEN data; IF (typescript ← ts) = NIL THEN systemDS.put ← DPutChar ELSE BEGIN data.startLine ← StreamDefs.GetPosition[ts]; systemDS.put ← DPutCharTS; END; RETURN END; GetTypeScript: PUBLIC PROCEDURE RETURNS [StreamDefs.DiskHandle] = BEGIN RETURN[typescript] END; BlinkCursor: PUBLIC PROCEDURE RETURNS [BOOLEAN] = BEGIN OPEN InlineDefs, data; 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 OPEN data; IF data = NIL THEN CreateDisplayData[]; IF font # NIL THEN font.destroy[font]; SetFont[f]; SetDummyDisplaySize[dummySize]; SetSystemDisplaySize[textLines, nPages]; RETURN END; CreateDisplayData[]; END.