-- UserTerminalHeadDorado.mesa
-- Last Edited by: Taft, February 27, 1983 3:27 pm

DIRECTORY
  BitBlt USING [BBTable],
  DoradoInputOutput USING [RWMufMan, savedCursor, SetDisplayFieldRate],
  DeviceCleanup USING [Await, Item, Reason],
  DisplayFace USING [Background, CursorPtr, GlobalStatePtr, Point],
  DisplayFaceExtras USING [FieldRate, MonitorType],
  Environment USING [bitsPerWord, first64K, PageCount, PageNumber, wordsPerPage],
  HeadStartChain USING [Start],
  Inline USING [BITOR, LongMult, LowHalf],
  KeyboardFace USING [],
  KeyStations USING [KeyBits],
  MouseFace USING [Buttons, Point],
  ProcessorFace USING [GetClockPulses, microsecondsPerHundredPulses],
  RuntimeInternal USING [WorryCallDebugger],
  SoundGenerator USING [];

UserTerminalHeadDorado: PROGRAM
  IMPORTS
    DeviceCleanup, DoradoInputOutput, RemainingHeads: HeadStartChain, Inline,
    ProcessorFace, RuntimeInternal
  EXPORTS
    DisplayFace, DisplayFaceExtras, HeadStartChain, KeyboardFace, MouseFace, ProcessorFace,
    SoundGenerator =
  BEGIN

  ErrorHalt: PROCEDURE = {
    RuntimeInternal.WorryCallDebugger["Error in UserTerminalHeadDorado"L]};

  --
  -- DisplayFace

  CSBPtr: TYPE = LONG POINTER TO CSBState;
  CSBState: TYPE = MACHINE DEPENDENT RECORD [
    dcbChainHead: PDCB, wakeupMask: WORD];
  csbPtr: CSBPtr = LOOPHOLE[LONG[420B]];  -- SHOULD MOVE TO IO PAGE

  PDCB: TYPE = POINTER TO DCB;
  DCB: TYPE = MACHINE DEPENDENT RECORD [  -- address must be even
    next: PDCB,
    resolution: {high, low},
    background: DisplayFace.Background,
    indenting: [0..77B],  -- in units of 16 bits
    width: [0..377B],  -- in units of 16 bits; must be even
    shortBitmap: POINTER,  -- must be even
    tag: {short, long},
    height: [0..77777B],  -- in double scan lines
    longBitmap: LONG POINTER];  -- must be even

  longBitMapSeal: POINTER = LOOPHOLE[177423B]; -- required value of DCB.shortBitmap

  displayState: {disconnected, off, on} ← disconnected;
  bitmapBase: LONG POINTER;  -- undefined if displayState=disconnected

  pDCBReal: LONG POINTER TO DCB;
  pDCBBlank: LONG POINTER TO DCB;
  pDCBNull: PDCB = LOOPHOLE[0];

  hasBuffer: PUBLIC BOOLEAN ← FALSE;

  pagesForBitmap: PUBLIC Environment.PageCount;  -- see initialization below

  Connect: PUBLIC PROCEDURE [bitmap: Environment.PageNumber] =
    BEGIN
    displayState ← off;
    bitmapBase ← LOOPHOLE[Inline.LongMult[bitmap, Environment.wordsPerPage]];
    -- LongPointerFromPage
    csbPtr.dcbChainHead ← Inline.LowHalf[pDCBBlank];  -- DCB's are in first64K
    END;

  Disconnect: PUBLIC PROCEDURE = {
    displayState ← disconnected; csbPtr.dcbChainHead ← pDCBNull};

  TurnOn: PUBLIC PROCEDURE =
    BEGIN
    IF displayState = disconnected THEN ErrorHalt[];
    displayState ← on;
    pDCBReal.longBitmap ← bitmapBase;
    csbPtr.dcbChainHead ← Inline.LowHalf[pDCBReal]  -- DCB's are in first64K
    END;

  TurnOff: PUBLIC PROCEDURE =
    BEGIN
    IF displayState = disconnected THEN ErrorHalt[];
    displayState ← off;
    csbPtr.dcbChainHead ← Inline.LowHalf[pDCBBlank];  -- DCB's are in first64K
    END;

  GetBitBltTable: PUBLIC PROCEDURE RETURNS [BitBlt.BBTable] =
    BEGIN
    IF displayState = disconnected THEN ErrorHalt[];
    RETURN[
      [
        dst: [word: bitmapBase, bit: 0], dstBpl: bitmapWidth,
        src: [word: bitmapBase, bit: 0], srcDesc: [srcBpl[bitmapWidth]],
        width: width, height: height, flags: []]];
    END;

  SetBackground: PUBLIC PROCEDURE [background: DisplayFace.Background] = {
    pDCBReal.background ← pDCBBlank.background ← background};

  -- See Start for initialization:
  width: PUBLIC CARDINAL [0..32767] ← lfWordsPerLine*Environment.bitsPerWord;
  cslWordsPerLine: CARDINAL = 38;
  lfWordsPerLine: CARDINAL = 64;
  torWordsPerLine: CARDINAL = 40;
  -- Size of bitmap, possibly large than screen image (initialized in Start):
  bitmapWidth: CARDINAL ← lfWordsPerLine*Environment.bitsPerWord;

  -- See Start for initialization:
  height: PUBLIC CARDINAL [0..32767] ← 808;  -- LF and CSL
  torHeight: CARDINAL = 800;

  pixelsPerInch: PUBLIC CARDINAL ← 72;

  -- See Start for initialization:
  refreshRate: PUBLIC CARDINAL ← lfRefreshRate;  -- frames (two fields) per second
  cslRefreshRate: CARDINAL = 30;
  lfRefreshRate: CARDINAL = 38;  -- actually ??

  interlaced: PUBLIC BOOLEAN ← TRUE;

  -- Scan line wakeups

  -- SetScanLineWakeup not implemented.

  -- GetScanLine not implemented.

  -- Boarder pattern

  hasBorder: PUBLIC BOOLEAN ← FALSE;

  SetBorderPattern: PUBLIC PROCEDURE [oddPairs, evenPairs: [0..377B]] = {};

  -- Cursor

  pHardwareCursor: DisplayFace.CursorPtr = LOOPHOLE[LONG[431B]];

  SetCursorPattern: PUBLIC PROCEDURE [cursorPtr: DisplayFace.CursorPtr] =
    BEGIN pHardwareCursor↑ ← cursorPtr↑; DoradoInputOutput.savedCursor ← cursorPtr↑; END;

  cursorPosition: PUBLIC LONG POINTER TO DisplayFace.Point ← LOOPHOLE[LONG[426B]];

  -- Initialization

  globalStateSize: PUBLIC CARDINAL ← SIZE[DCB]*2;

  InitializeCleanup: PUBLIC PROCEDURE =
    BEGIN OPEN DeviceCleanup;
    item: Item;
    reason: Reason;
    state: CSBState;
    mouseCoord: MouseFace.Point;
    cursorCoord: DisplayFace.Point;
    timeDone: LONG CARDINAL;
    maxPulsesPerRefresh: LONG CARDINAL =
      2*  --for safety--
        (LONG[100]* --pulsesPerHundredPulses-- 1000000  --microsecondsPerSecond--
          )/(LONG[refreshRate]  --framesPerSecond--
               *ProcessorFace.microsecondsPerHundredPulses);
    DO
      reason ← Await[@item];
      SELECT reason FROM
        turnOff =>
          BEGIN
          state ← csbPtr↑;
          mouseCoord ← mouse↑;
          cursorCoord ← cursorPosition↑;
          -- copy of cursor pattern itself is saved by SetCursorPattern
          csbPtr.dcbChainHead ← pDCBNull;
          timeDone ← ProcessorFace.GetClockPulses[];
          WHILE ProcessorFace.GetClockPulses[] - timeDone < maxPulsesPerRefresh DO
            ENDLOOP;
          END;
        turnOn =>
          BEGIN
          mouse↑ ← mouseCoord;
          cursorPosition↑ ← cursorCoord;
          -- cursor pattern itself is restored by SetMP trap handler in ProcessorHeadDorado
          csbPtr↑ ← state;
          END;
        ENDCASE;
      ENDLOOP;
    END;

  Initialize: PUBLIC PROCEDURE [
    globalState: DisplayFace.GlobalStatePtr, wakeVF: WORD] =
    BEGIN
    dcb: DCB = [
      next: pDCBNull, resolution: high, background: white, indenting: 0, width: 0,
      shortBitmap: longBitMapSeal, tag: long, height: 0, longBitmap: NIL];
    csbPtr.wakeupMask ← Inline.BITOR[wakeVF, csbPtr.wakeupMask];
    pDCBBlank ← LOOPHOLE[@Environment.first64K[globalState]];
    pDCBReal ← LOOPHOLE[@Environment.first64K[globalState] + SIZE[DCB]];
    pDCBBlank↑ ← pDCBReal↑ ← dcb;
    pDCBReal.width ← width/Environment.bitsPerWord;
    pDCBReal.height ← height/2;
    END;


  --  DisplayFaceExtras.

  monitorType: PUBLIC DisplayFaceExtras.MonitorType ← lf;  -- changed in Start

  SetFieldRate: PUBLIC PROCEDURE [rate: DisplayFaceExtras.FieldRate]
    RETURNS [ok: BOOLEAN] =
    BEGIN
    SELECT monitorType FROM
      alto =>
        RETURN [rate=normalAlto];
      lf =>
        BEGIN
        SELECT rate FROM
          normalLF =>
            DoradoInputOutput.SetDisplayFieldRate[sync: 18, visible: 430, topBorder: 14];
          lfBall60hz =>
            DoradoInputOutput.SetDisplayFieldRate[sync: 18, visible: 560, topBorder: 14];
          lfPhillips60hz =>
            DoradoInputOutput.SetDisplayFieldRate[sync: 58, visible: 520, topBorder: 25];
          ENDCASE =>
            RETURN [FALSE];
        RETURN [TRUE];
        END;
      ENDCASE;
    END;

  SetVerticalWaveforms: PUBLIC PROCEDURE [sync, visible, topBorder: CARDINAL]
    RETURNS [ok: BOOLEAN] =
    BEGIN
    DoradoInputOutput.SetDisplayFieldRate[
      sync: sync, visible: visible, topBorder: topBorder];
    RETURN [TRUE];
    END;


  --
  --  HeadStartChain

  Start: PUBLIC PROCEDURE =
    BEGIN
    displayIsLF: BOOLEAN =
      (DoradoInputOutput.RWMufMan[[useDMD: FALSE, dMuxAddr: 3106B]].dMuxData # 0);
    IF ~displayIsLF THEN
      BEGIN
      monitorType ← alto;
      bitmapWidth ← width ← cslWordsPerLine*Environment.bitsPerWord;
      refreshRate ← cslRefreshRate;
      millisecondsPerTick ← cslMillisecondsPerTick;
      END;
    pagesForBitmap ←
      ((bitmapWidth/Environment.bitsPerWord)*height + Environment.wordsPerPage -
         1)/Environment.wordsPerPage;
    csbPtr.dcbChainHead ← pDCBNull;  -- DCB's are in first64K
    RemainingHeads.Start[];
    END;

  --
  -- KeyboardFace

  keyboard: PUBLIC LONG POINTER TO READONLY KeyStations.KeyBits ← LOOPHOLE[LONG[
    177033B]];

  --
  -- MouseFace

  position: PUBLIC LONG POINTER TO READONLY MouseFace.Point ← mouse;
  SetPosition: PUBLIC PROCEDURE [newMousePosition: MouseFace.Point] =
    BEGIN mouse↑ ← newMousePosition END;
  buttons: PUBLIC LONG POINTER TO READONLY MouseFace.Buttons ← LOOPHOLE[keyboard];
  mouse: LONG POINTER TO MouseFace.Point = LOOPHOLE[LONG[424B]];

  --
  -- ProcessorFace

  -- See main body for initialization:
  millisecondsPerTick: PUBLIC CARDINAL ← lfMillisecondsPerTick;
  cslMillisecondsPerTick: CARDINAL = 50;
  lfMillisecondsPerTick: CARDINAL = 40;  -- actually 39.7
  torMillisecondsPerTick: CARDINAL = 40;  -- what should this  be?

  --
  -- SoundGenerator (dummy implementation)

  Beep: PUBLIC PROCEDURE [frequency: CARDINAL, duration: CARDINAL] = {};

  END.

February 6, 1980  3:55 PM	Gobbel	Create file from UserTerminalImpl
February 8, 1980  12:29 PM	McJones	Start chaining
February 25, 1980  1:46 PM	McJones	Automatic determination of LF/CSL/Tor display
March 7, 1980  5:56 PM	McJones	Wait for two frames in turnOff arm of cleanup procedure
June 26, 1980  11:04 AM	McJones	OISProcessorFace=>ProcessorFace; allow Disconnect in disconnected state; add cursorPosition, mousePosition
July 29, 1980  9:54 AM	McJones	Add keyboard, hasBorder; split off MouseFace
July 30, 1980  6:21 PM	McJones	Add pagesForBitmap, GetBitBltTables; buffered=>hasBuffer
January 28, 1981  9:27 AM	McJones	Dummy SoundGenerator
March 21, 1981  4:51 PM	Taft	Convert for Dorado
 9-Jun-81 18:04:35	Taft	LF display
February 27, 1983 2:12 pm  Taft  DisplayFaceExtras