-- ColorDisplayHeadD0.mesa
-- Last edit by Doug Wyatt on 21-Dec-81  9:40:49
-- Last edit by Levin on 26-Feb-82 13:12:24

DIRECTORY
  ColorDisplayFace USING [Color, Mode],
  DeviceCleanup USING [Await, Item, Reason],
  D0InputOutput USING [ControllerNumber, ControllerType, GetNextController, IOPage,
    nullControllerNumber, Output],
  Environment USING [Base, PageCount, PageNumber, wordsPerPage],
  HeadStartChain USING [Start],
  Inline USING [LongDivMod, LongMult],
  ProcessorFace USING [GetClockPulses, microsecondsPerHundredPulses],
  RuntimeInternal USING [WorryCallDebugger];

ColorDisplayHeadD0: PROGRAM
IMPORTS DeviceCleanup, D0InputOutput, RemainingHeads: HeadStartChain,
  Inline, ProcessorFace, RuntimeInternal
EXPORTS ColorDisplayFace, HeadStartChain = {

ErrorHalt: PROC = {RuntimeInternal.WorryCallDebugger["Error in ColorDisplayHeadD0"L]};

RPtr: TYPE = Environment.Base RELATIVE POINTER; -- relative to absolute address 0
rpNIL: RPtr = LOOPHOLE[0];

Mode: TYPE = ColorDisplayFace.Mode;
Color: TYPE = ColorDisplayFace.Color;

screenwidth: CARDINAL = 640; -- pixels per line
screenheight: CARDINAL = 481; -- scan lines (part of last line is not visible)

wordsPerLine: CARDINAL = screenwidth/4;
screenSize: LONG CARDINAL = Inline.LongMult[wordsPerLine,screenheight];

ColorData: TYPE = MACHINE DEPENDENT RECORD [
  addr: [0..17B], r,g,b,unused: BOOLEAN ← FALSE, data: [0..377B]];

ColorEntry: TYPE = MACHINE DEPENDENT RECORD [r,g,b: ColorData];

ColorTable: TYPE = MACHINE DEPENDENT RECORD [
  front: ARRAY [0..2) OF CARDINAL ← ALL[0],
  array: ARRAY [0..16) OF ColorEntry,
  back: ARRAY  [0..10) OF CARDINAL ← ALL[0]
  ];
tableSize: CARDINAL = ((SIZE[ColorTable]+15)/16)*16; -- round up to multiple of 16

CSBState: TYPE = MACHINE DEPENDENT RECORD [
  bitmap: LONG POINTER, -- must be a multiple of 16
  table: LONG POINTER TO ColorTable -- must be a multiple of 16
  ];

colorControllerType: D0InputOutput.ControllerType = 257B;
colorControllerNumber: D0InputOutput.ControllerNumber ← D0InputOutput.nullControllerNumber;
csb: LONG POINTER TO CSBState ← NIL;

-- Interface variables

-- The following are valid as soon as the module start code has run (see Start)

globalStateSize: PUBLIC CARDINAL ← 0; -- for Initialize
displayType: PUBLIC CARDINAL ← 0; -- display type; 0 means display not available
Ramtek525: CARDINAL = 1; -- displayType for Ramtek 525 line monitor

pixelsPerInch: PUBLIC CARDINAL ← 0; -- Size of a pixel
mixedRG: PUBLIC BOOLEAN ← FALSE; -- in fullmode, red and green alternate in A bitmap

width,height: PUBLIC CARDINAL ← 0; -- Dimensions of current raster in pixels
baseA,baseB,baseC: PUBLIC LONG POINTER ← NIL; -- bitmap addresses
bplA,bplB,bplC: PUBLIC CARDINAL ← 0; -- bitmap bits per line

-- Internal globals

initialized: BOOLEAN ← FALSE; -- control blocks are allocated
connected: BOOLEAN ← FALSE; -- bitmaps and tables are allocated, globals are set
turnedon: BOOLEAN ← FALSE; -- display is on, bitmaps and tables are resident
fullmode: BOOLEAN ← FALSE; -- connected in 24 bit per pixel mode
mapmode: BOOLEAN ← FALSE; -- connected in a mapped mode

-- The following globals are valid when initialized=TRUE (see Initialize)

-- (none for D0) --

-- The following globals are valid when connected=TRUE (see Connect)

showA: BOOLEAN ← FALSE; -- to be shown

table: LONG POINTER TO ColorTable ← NIL;

Initialize: PUBLIC PROC [globalState: RPtr] = {
  initialized ← TRUE;
  };

Lg: TYPE = [0..4); -- logarithm base 2
lg1: Lg = 0; lg2: Lg = 1; lg4: Lg = 2; lg8: Lg = 3;

PagesForWords: PROC[words: LONG CARDINAL] RETURNS[CARDINAL] = INLINE {
  q,r: CARDINAL; [q,r] ← Inline.LongDivMod[words,Environment.wordsPerPage];
  RETURN[IF r>0 THEN q+1 ELSE q] };

WordsForPages: PROC[pages: CARDINAL] RETURNS[LONG CARDINAL] = INLINE {
  RETURN[Inline.LongMult[pages,Environment.wordsPerPage]] };

LPFromPage: PROC[page: Environment.PageNumber] RETURNS[LONG POINTER] = INLINE {
  RETURN[LOOPHOLE[Inline.LongMult[page, Environment.wordsPerPage]]] };

HasMode: PUBLIC PROC[mode: Mode] RETURNS[BOOLEAN] = {
  IF displayType=0 THEN RETURN[FALSE];
  IF mode.full OR mode.useB THEN RETURN[FALSE];
  RETURN[mode.useA AND mode.lgBitsPerPixelA=lg4];
  };

PagesForMode: PUBLIC PROC[mode: Mode] RETURNS[Environment.PageCount] = {
  RETURN[PagesForWords[tableSize + screenSize]];
  };

Connect: PUBLIC PROC [mode: Mode,
  firstPage: Environment.PageNumber, nPages: Environment.PageCount] = {
  alloc: LONG POINTER ← LPFromPage[firstPage]; -- next available word
  Alloc: PROC[words: LONG CARDINAL] RETURNS[LONG POINTER] = INLINE {
    p: LONG POINTER ← alloc; alloc ← alloc + words; RETURN[p] };
  wpl: CARDINAL = screenwidth/4; -- words per scan line
  IF NOT initialized OR NOT HasMode[mode] OR connected THEN ErrorHalt[];
  IF nPages<PagesForMode[mode] THEN ErrorHalt[];
  width ← screenwidth;
  height ← screenheight;
  bplA ← 4*screenwidth;
  -- Note: both the following pointers must be multiples of 16.
  -- table is a multiple of 16, since it starts at a page boundary
  -- baseA is a multiple of 16, since tableSize is a multiple of 16
  -- Also, the color table must not cross a 64K boundary; should be OK
  -- here since table starts at a page boundary and tableSize<wordsPerPage.
  table ← Alloc[tableSize];
  baseA ← Alloc[screenSize];

  table↑ ← [front: ALL[0], array: , back: ALL[0]];
  FOR i: CARDINAL IN [0..16) DO
    table.array[i].r ← [addr: i, r: TRUE, data: 255];
    table.array[i].g ← [addr: i, g: TRUE, data: 255];
    table.array[i].b ← [addr: i, b: TRUE, data: 255];
    ENDLOOP;
  csb.table ← table;
  showA ← TRUE;
  connected ← TRUE;
  };

Disconnect: PUBLIC PROC = {
  IF turnedon THEN TurnOff[];
  csb.table ← NIL;
  width ← height ← 0;
  baseA ← NIL; bplA ← 0;
  table ← NIL;
  connected ← FALSE;
  };

TurnOn: PUBLIC PROC = {
  IF (NOT connected) OR turnedon THEN RETURN;
  IF showA THEN { csb.bitmap ← baseA; HardwareOn[] };
  turnedon ← TRUE;
  };

TurnOff: PUBLIC PROC = {
  IF NOT turnedon THEN RETURN;
  IF showA THEN { csb.bitmap ← NIL; Wait[]; HardwareOff[] };
  turnedon ← FALSE;
  };


Show: PUBLIC PROC[a,b,c: BOOLEAN] = {
  IF showA=a THEN RETURN;
  showA ← a;
  IF NOT turnedon THEN RETURN;
  IF showA THEN { csb.bitmap ← baseA; HardwareOn[] }
  ELSE { csb.bitmap ← NIL; Wait[]; HardwareOff[] };
  };

-- For D0 color map, 0 = white, 255 = black!

GetColor: PUBLIC PROC[pixelA,pixelB: CARDINAL] RETURNS[r,g,b: Color] = {
  IF connected THEN {
    index: [0..16) ← pixelA MOD 16;
    entry: ColorEntry ← table.array[index];
    RETURN[r: 255 - entry.r.data, g: 255 - entry.g.data, b: 255 - entry.b.data]
    }
  ELSE RETURN[0,0,0];
  };

SetColor: PUBLIC PROC[pixelA,pixelB: CARDINAL, r,g,b: Color] = {
  IF connected THEN {
    index: [0..16) ← pixelA MOD 16;
    table.array[index].r.data ← 255 - r;
    table.array[index].g.data ← 255 - g;
    table.array[index].b.data ← 255 - b;
    };
  };

GetRedMap: PUBLIC PROC[in: Color] RETURNS[out: Color] = { RETURN[0] };
GetGreenMap: PUBLIC PROC[in: Color] RETURNS[out: Color] = { RETURN[0] };
GetBlueMap: PUBLIC PROC[in: Color] RETURNS[out: Color] = { RETURN[0] };

SetRedMap: PUBLIC PROC[in,out: Color] = { };
SetGreenMap: PUBLIC PROC[in,out: Color] = { };
SetBlueMap: PUBLIC PROC[in,out: Color] = { };

-- Initialization

minFPS: CARDINAL = 30; -- minimum number of frames per second
maxPulsesPerRefresh: LONG CARDINAL = 2*  --for safety--
  (LONG[100]* --pulsesPerHundredPulses-- 1000000  --microsecondsPerSecond--)/
    (LONG[minFPS]*ProcessorFace.microsecondsPerHundredPulses);

Wait: PROC = INLINE { timeDone: LONG CARDINAL ← ProcessorFace.GetClockPulses[];
  WHILE ProcessorFace.GetClockPulses[] - timeDone < maxPulsesPerRefresh DO ENDLOOP };

HardwareOn: PROC = INLINE {
  D0InputOutput.Output[datum: 6, register:
    [controller: colorControllerNumber, register: 0]] };

HardwareOff: PROC = INLINE {
  D0InputOutput.Output[datum: 0, register:
    [controller: colorControllerNumber, register: 0]] };

InitializeCleanup: PUBLIC PROC = { OPEN DeviceCleanup;
  item: Item;
  reason: Reason;
  state: CSBState;
  DO
    reason ← Await[@item];
    IF csb = NIL THEN LOOP;   -- Driver doesn't know that there isn't a color display!  (RL)
    SELECT reason FROM
      turnOff => { state ← csb↑;
        IF state.bitmap#NIL THEN { csb.bitmap ← NIL; Wait[]; HardwareOff[] };
	};
      turnOn => { csb↑ ← state;
        IF state.bitmap#NIL THEN HardwareOn[];
	};
      ENDCASE;
    ENDLOOP;
  };

--  HeadStartChain

Start: PUBLIC PROC = {
  OPEN D0InputOutput;
  colorControllerNumber ← GetNextController[colorControllerType,nullControllerNumber];
  IF colorControllerNumber#nullControllerNumber THEN {
    displayType ← Ramtek525;
    pixelsPerInch ← 72; -- *** Is this right? ***
    csb ← LOOPHOLE[@IOPage[colorControllerNumber]];
    csb↑ ← [bitmap: NIL, table: NIL];
    };
  RemainingHeads.Start[];
  };

}.