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

DIRECTORY
  BitBlt USING [BBTable],
  D0InputOutput USING [
    ControllerNumber, ControllerType, GetNextController, Input, null,
    nullControllerNumber, uibScb, utvfc],
  DeviceCleanup USING [Await, Item, Reason],
  DisplayFace USING [Background, Cursor, 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 [];

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

  ErrorHalt: PROCEDURE =
    {RuntimeInternal.WorryCallDebugger["Error in UserTerminalHeadD0"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

  UTVFCControlRegister: TYPE = MACHINE DEPENDENT RECORD [
    -- not currently used
    controlNibbles: [0..377B] ← 0,
    IncNC: [0..1] ← 0,
    PPBckGnd: [0..3] ← 0,
    PPBlank, PPVS, PreOField, AllowWU, ClrNC: [0..1] ← 0];

  UTVFCInputRegister: TYPE = MACHINE DEPENDENT RECORD [
    controllerIDa: [0..377B] ← 2,
    bitClockRate: [0..37B],
    controllerIDb: [0..3] ← 2,
    test: [0..1]];
  cslBitClockRate: [0..37B] = 5B;
  lfBitClockRate: [0..37B] = 3B;

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

  controllerType: D0InputOutput.ControllerType;
  controller: D0InputOutput.ControllerNumber;
  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.

	-- Border pattern

  hasBorder: PUBLIC BOOLEAN ← FALSE;

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

	-- Cursor

  pHardwareCursor: DisplayFace.CursorPtr = LOOPHOLE[LONG[431B]];
    -- SHOULD BE IN IO PAGE

  SetCursorPattern: PUBLIC PROCEDURE [cursorPtr: DisplayFace.CursorPtr] =
    BEGIN pHardwareCursor↑ ← 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;
    cursor: DisplayFace.Cursor;
    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↑;
	  cursor ← pHardwareCursor↑;
	  csbPtr.dcbChainHead ← pDCBNull;
	  timeDone ← ProcessorFace.GetClockPulses[];
	  WHILE ProcessorFace.GetClockPulses[] - timeDone < maxPulsesPerRefresh DO
	    ENDLOOP;
	  END;
	turnOn =>
	  BEGIN
	  mouse↑ ← mouseCoord;
	  cursorPosition↑ ← cursorCoord;
	  pHardwareCursor↑ ← cursor;
	  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: NIL, 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
    RETURN [
      SELECT monitorType FROM
        alto => rate=normalAlto,
        lf => rate=normalLF,
        ENDCASE => FALSE];
    END;

  SetVerticalWaveforms: PUBLIC PROCEDURE [sync, visible, topBorder: CARDINAL]
    RETURNS [ok: BOOLEAN] =
    {RETURN [FALSE]};

  --
  --  HeadStartChain

  Start: PUBLIC PROCEDURE =
    BEGIN OPEN D0InputOutput;
    IF (controller ← GetNextController[utvfc, nullControllerNumber]) ~=
      nullControllerNumber THEN
      BEGIN
      inputReg: UTVFCInputRegister = Input[[controller: controller, register: 0]];
      controllerType ← utvfc;
      IF inputReg.bitClockRate = cslBitClockRate THEN
	BEGIN
	monitorType ← alto;
	bitmapWidth ← width ← cslWordsPerLine*Environment.bitsPerWord;
	refreshRate ← cslRefreshRate;
	millisecondsPerTick ← cslMillisecondsPerTick;
	END;
      END
    ELSE
      IF (controller ← GetNextController[uibScb, nullControllerNumber]) ~=
	nullControllerNumber THEN
	BEGIN
	monitorType ← lf;
	controllerType ← uibScb;
	width ← torWordsPerLine*Environment.bitsPerWord;
	bitmapWidth ← torWordsPerLine*Environment.bitsPerWord;
	height ← torHeight;
	millisecondsPerTick ← torMillisecondsPerTick;
	END
      ELSE controllerType ← null;
    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 24, 1981  3:06 PM	Jose	Correct width for Tor.
February 27, 1983 2:12 pm  Taft  DisplayFaceExtras