-- 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.