-- EikonixProtocolImpl.mesa
-- Last edited by Tim Diebert:   14-May-86  9:39:24
-- Copyright (C) 1985, 1986 Xerox Corporation.  All rights reserved.

-- This module is based on EikonixProtocol.mesa
--   of January 27, 1986 9:21:56 am PST.
-- If it is necessary to make changes to SampleLength this code must be changed!

DIRECTORY
  DicentraInputOutput USING [IOAddress, Output, RawRead, RawWrite, ReadHyper],
  EikonixProtocol,
  Environment USING [Block],
  Inline USING [BITAND, BITROTATE, BITSHIFT, DBITAND, DBITSHIFT, HighByte],
  Multibus USING [RawReadBlock, wordsPerBlock],
  MultibusAddresses USING [scc3],
  Process USING [Pause, SecondsToTicks, Yield],
  PupStream USING [CloseReason, CreatePupByteStreamListener, PupAddress,
    PupListener, RejectThisRequest, SecondsToTocks, StreamClosing],
  Space USING [PagesFromWords, ScratchMap],
  Stream USING [Delete, GetChar, GetWord, Handle, PutBlock, PutChar, PutWord,
    SendNow, TimeOut],
  Watchdog USING [Reactivate]
  ;

EikonixProtocolImpl: PROGRAM
  IMPORTS DicentraInputOutput, Inline, Multibus, Space, Stream, Process,
    PupStream, Watchdog
  EXPORTS 
  = BEGIN OPEN EikonixProtocol;
  
  listener: PupStream.PupListener ← NIL;
  active: BOOL ← FALSE;
  
  Buffer: TYPE = RECORD
    [blockPointer: LONG POINTER TO ARRAY [0 .. EikonixProtocol.Width) OF WORD,
    startIndex: CARDINAL ← EikonixProtocol.Width * 2,
    stopIndexPlusOne: CARDINAL ← 0];
  bufferA: Buffer;
  bufferB: Buffer;
  dc: Buffer;
  gain: Buffer;
  longCardBuffer: LONG POINTER TO ARRAY [0 .. EikonixProtocol.Width)
    OF LONG CARDINAL;
    
  MyAbort: ERROR = CODE;
  
  Init: PROC [] = BEGIN
    cmd: EikonixCommand ← [reg: Stage, bits: 80H];
    MBWrite[deviceCommandRegister, LOOPHOLE [cmd]];
--    cmd ← [reg: LOOPHOLE[3], bits: 0];
--    MBWrite[deviceCommandRegister, LOOPHOLE [cmd]];
    active ← FALSE;
    longCardBuffer ← New[EikonixProtocol.Width * SIZE[LONG CARDINAL]];
    bufferA.blockPointer ← New[EikonixProtocol.Width];
    bufferB.blockPointer ← New[EikonixProtocol.Width];
    bufferA.blockPointer↑ ← ALL[0];
    bufferA.startIndex ← 0;
    bufferA.stopIndexPlusOne ← EikonixProtocol.Width * 2;
    bufferB.blockPointer↑ ← ALL[0];
    bufferB.startIndex ← 0;
    bufferB.stopIndexPlusOne ← EikonixProtocol.Width * 2;
    dc.blockPointer ← New[EikonixProtocol.Width];
    dc.blockPointer↑ ← ALL[0];
    dc.startIndex ← 0;
    dc.stopIndexPlusOne ← EikonixProtocol.Width * 2;
    gain.blockPointer ← New[EikonixProtocol.Width];
    gain.blockPointer↑ ← ALL[08000H];  -- Really 800H shifted 4 left.
    gain.startIndex ← 0;
    gain.stopIndexPlusOne ← EikonixProtocol.Width * 2;
    LoadIt[];
--    MBWrite[functionControlRegister, 0100h];
    IF listener = NIL THEN listener ← PupStream.CreatePupByteStreamListener[
      local: LOOPHOLE[EikonixProtocol.xdeScannerSocket], 
      proc: ScannerCommand,
      ticks: PupStream.SecondsToTocks[30*60],
      filter: CheckScannerBusy];
    END;
  
  ScannerCommand: PROC [stream: Stream.Handle, pupAddress: PupStream.PupAddress]
    = BEGIN
    command: EikonixProtocol.Command;
    BEGIN
      ENABLE {PupStream.StreamClosing, Stream.TimeOut, MyAbort,
        ABORTED => GOTO Exit};
      active ← TRUE;
      DO
        command ← GetCommand[stream];
        SELECT command FROM
          SetCommandReg => SetCommandReg[stream];
          SetDeviceCommandReg => SetDeviceCommandReg[stream];
          GetStatus => GetStatus[stream];
          GetDeviceStatus => GetDeviceStatus[stream];
          SetFCR => SetFCR[stream];
          SetDMAData => SetDMAData[stream];
          SendDataBuffer0 => SendDataBuffer[stream, 0];
          SendDataBuffer1 => SendDataBuffer[stream, 1];
          TurnOnLights => TurnOnLights[stream];
          TurnOffLights => TurnOffLights[stream];
          SetFilter => SetFilter[stream];
          ScanPage => ScanPage[stream];
          SampleScan => SampleScan[stream];
          LoadNormalizer => LoadNormalizer[stream];
	  ScanMaxMin => ScanMaxMin[stream];
	  ComputeCal => ComputeCal[stream];
          ENDCASE => { SendError[stream]; GOTO Exit};
        Stream.SendNow[stream];
        ENDLOOP;
      END;
      EXITS Exit => {Stream.Delete[stream]; KillLights[]; active ← FALSE; RETURN};
    END;
  
  GetCommand: PROC [stream: Stream.Handle] RETURNS [cmd: EikonixProtocol.Command]
    = BEGIN
    [] ← Stream.GetChar[stream];
    cmd ← LOOPHOLE[Stream.GetChar[stream]];
    RETURN[cmd];
    END;
  
  SetCommandReg: PROC [stream: Stream.Handle] =  BEGIN
    word: WORD ← LOOPHOLE [Stream.GetWord[stream]];
    MBWrite[commandRegister, word];
    EchoCommand[stream, SetCommandReg];
    END;
    
  SetDeviceCommandReg: PROC [stream: Stream.Handle] =  BEGIN
    word: WORD ← LOOPHOLE [Stream.GetWord[stream]];
    MBWrite[deviceCommandRegister, word];
    EchoCommand[stream, SetDeviceCommandReg];
    END;
 
  GetStatus: PROC [stream: Stream.Handle] =  BEGIN
    word: WORD ← MBRead[statusRegister];
    EchoCommand[stream, GetStatus];
    Stream.PutWord[stream, word];
    END;
 
  GetDeviceStatus: PROC [stream: Stream.Handle] =  BEGIN
    word: WORD ← MBRead[deviceCommandRegister];
    EchoCommand[stream, GetDeviceStatus];
    Stream.PutWord[stream, word];
    END;
    
  SetFCR: PROC [stream: Stream.Handle] =  BEGIN
    word: WORD ← LOOPHOLE [Stream.GetWord[stream]];
    MBWrite[functionControlRegister, word];
    EchoCommand[stream, SetFCR];
    END;
    
  SetDMAData: PROC [stream: Stream.Handle] =  BEGIN
    word: WORD ← LOOPHOLE [Stream.GetWord[stream]];
    MBWrite[dataRegister, word];
    EchoCommand[stream, SetDMAData];
    END;
    
  SendDataBuffer: PROC [stream: Stream.Handle, buf: CARDINAL] =  BEGIN
    a: Address ← IF buf = 0 THEN dataBuffer0 ELSE dataBuffer1;
    EchoCommand[stream, (IF buf = 0 THEN SendDataBuffer0 ELSE SendDataBuffer1)];
    DicentraInputOutput.ReadHyper
      [to: @bufferA.blockPointer↑, from: LOOPHOLE[a],
      words: Multibus.wordsPerBlock];
    bufferA.startIndex ← 0;
    bufferA.stopIndexPlusOne ← Multibus.wordsPerBlock * 2;
    Stream.PutBlock[stream, LOOPHOLE[bufferA]];
    END;
    
  TurnOnLights: PROC [stream: Stream.Handle] =  BEGIN
    DicentraInputOutput.Output[2, LOOPHOLE [MultibusAddresses.scc3 + 5B]];
      -- RTS on port 7.
    Process.Pause[Process.SecondsToTicks[2]];  -- Wait for the lights to come up.
    EchoCommand[stream, TurnOnLights];
    END;
  
  TurnOffLights: PROC [stream: Stream.Handle] =  BEGIN
    KillLights[];
    EchoCommand[stream, TurnOffLights];
    END;

  ScanPage: PROC [stream: Stream.Handle] =  BEGIN
    sendBuffer: Address ← dataBuffer0; scanBuffer: Address ← dataBuffer1;
    tempBuffer: Address;
    scanStart: CARDINAL ← Stream.GetWord[stream];
    pixelStart: CARDINAL ← Stream.GetWord[stream];
    numScans: CARDINAL ← Stream.GetWord[stream];
    numPixel: CARDINAL ← Stream.GetWord[stream];
    bit8: BOOL ← (Stream.GetWord[stream] = 0);
    cmd: EikonixCommand ← [reg: Stage, bits: 80H];
    MBWrite[deviceCommandRegister, LOOPHOLE [cmd]];
    IF scanStart + numScans > EikonixProtocol.Height THEN SendError[stream];
    IF pixelStart + numPixel > EikonixProtocol.Width THEN SendError[stream];
    EchoCommand[stream, ScanPage];
    Stream.PutWord[stream, numScans];
    Stream.PutWord[stream, numPixel];
    
    SetStagePosition[scanStart]; -- Set the starting line.
    BusyWait[];  -- and wait for it to move.
    FOR i: CARDINAL IN [scanStart .. scanStart + numScans) DO
       StartScan[scanBuffer, FALSE, bit8]; -- Start the scan @ i
       DataWait[];  -- wait for the scan line to become ready
       IF i + 1 < EikonixProtocol.Width THEN SetStagePosition[i+1];
       SendMBBuffer[stream, pixelStart, numPixel, scanBuffer, bit8];
       BusyWait[]; -- Wait for the stage to move.
       ENDLOOP;
     END;
  
  SampleScan: PROC [stream: Stream.Handle] =  BEGIN
    sendBuffer: Address ← dataBuffer0; scanBuffer: Address ← dataBuffer1;
    tempBuffer: Address;
    cnt: CARDINAL = EikonixProtocol.Height/SampleLength;
    first: BOOL ← TRUE;
    bit8: BOOL ← Stream.GetWord[stream] = 0;
    EchoCommand[stream, SampleScan];
    SetStagePosition[0];
    BusyWait[];
    StartScan[scanBuffer, FALSE, bit8];
    DataWait[];
    FOR i: CARDINAL IN [0 .. cnt) DO
      p: PROCESS;
      IF NOT first THEN
        p ← FORK SendSampledBuffer[stream, sendBuffer, bit8];
      -- Scan into scanBuffer buffer
      StartScan[scanBuffer, FALSE, bit8];
      DataWait[];
      SetStagePosition[Inline.BITSHIFT[i, 4]];
      BusyWait[];
      IF NOT first THEN JOIN p;
      first ← FALSE;
      tempBuffer ← sendBuffer; sendBuffer ← scanBuffer; scanBuffer ← tempBuffer;
      ENDLOOP;
    SendSampledBuffer[stream, sendBuffer, bit8];
    END;
  
  LoadNormalizer: PROC [stream: Stream.Handle] = BEGIN
    FOR i: CARDINAL IN [0 .. EikonixProtocol.Width) DO
      dc.blockPointer[i] ← Stream.GetWord[stream];
      ENDLOOP;
    FOR i: CARDINAL IN [0 .. EikonixProtocol.Width) DO
      gain.blockPointer[i] ← Stream.GetWord[stream];
      ENDLOOP;
    LoadIt[];
    EchoCommand[stream, LoadNormalizer];
    END;
  
  LoadIt: PROC [] = BEGIN
    BusyWait[];
    MBWrite[ functionControlRegister, toDarkCurrent];
    FOR i: CARDINAL IN [0 .. EikonixProtocol.Width) DO
      BusyWait[];
      MBWrite[dataRegister, dc.blockPointer[i]];
      ENDLOOP;
    BusyWait[];
    MBWrite[functionControlRegister, toGain];
    FOR i: CARDINAL IN [0 .. EikonixProtocol.Width) DO
      BusyWait[];
      MBWrite[dataRegister, gain.blockPointer[i]];
      ENDLOOP;
    END;

  ScanMaxMin: PROC [stream: Stream.Handle] = BEGIN
    useA: BOOL ← FALSE;
    first: BOOL ← TRUE;
    sendBuffer: Address ← dataBuffer0; scanBuffer: Address ← dataBuffer1;
    tempBuffer: Address;
    minScan, minPixel: CARDINAL ← 0;
    maxScan, maxPixel: CARDINAL ← 0;

    scanStart: CARDINAL ← Stream.GetWord[stream];
    pixelStart: CARDINAL ← Stream.GetWord[stream];
    numScans: CARDINAL ← Stream.GetWord[stream];
    numPixel: CARDINAL ← Stream.GetWord[stream];
    min: CARDINAL ← 8000H;
    max: CARDINAL ← 0;
    FindMinMax: PROC [b: Address, i: CARDINAL] = BEGIN
      FOR j: CARDINAL IN [pixelStart .. pixelStart + numPixel) DO
        w: CARDINAL ← Inline.BITAND[MBRead[b + j], 0FFFH];
	IF w < min THEN BEGIN
	  min ← w; minScan ← i; minPixel ← j;
	  END;
	IF w > max THEN BEGIN
	  max ← w; maxScan ← i; maxPixel ← j;
	  END;
        ENDLOOP;
      END;
    IF scanStart + numScans > EikonixProtocol.Height THEN SendError[stream];
    IF pixelStart + numPixel > EikonixProtocol.Width THEN SendError[stream];
    EchoCommand[stream, ScanMaxMin];
    SetStagePosition[scanStart];
    BusyWait[];
    FOR i: CARDINAL IN [scanStart .. scanStart + numScans) DO
      p: PROCESS;
      IF NOT first THEN
        p ← FORK FindMinMax[sendBuffer, i - 1];
      StartScan[scanBuffer, TRUE, FALSE];
      DataWait[];
      IF NOT first THEN JOIN p;
      first ← FALSE;
      tempBuffer ← sendBuffer; sendBuffer ← scanBuffer; scanBuffer ← tempBuffer;
--      Watchdog.Reactivate[3*60];
      ENDLOOP;
    FindMinMax[sendBuffer, scanStart + numScans - 1];
    Stream.PutWord[stream, min];
    Stream.PutWord[stream, minScan];
    Stream.PutWord[stream, minPixel];
    Stream.PutWord[stream, max];
    Stream.PutWord[stream, maxScan];
    Stream.PutWord[stream, maxPixel];
    Stream.SendNow[stream];
    END;

  ComputeCal: PROC [stream: Stream.Handle] = BEGIN
    sendBuffer: Address ← dataBuffer0; scanBuffer: Address ← dataBuffer1;
    tempBuffer: Address;
    longCardBuffer↑ ← ALL [0];
    SetStagePosition[512];
    BusyWait[];
    StartScan[scanBuffer, FALSE, FALSE];
    SumLongCard[scanBuffer];
    FOR i: CARDINAL IN [513 .. 1536) DO
       p: PROCESS;
       p ← FORK SumLongCard[sendBuffer];
       StartScan[scanBuffer, FALSE, FALSE];
       DataWait[];
       SetStagePosition[i];
       SumLongCard[scanBuffer];
       BusyWait[];
       JOIN p;
       tempBuffer ← sendBuffer; sendBuffer ← scanBuffer; scanBuffer ← tempBuffer;
       Watchdog.Reactivate[3*60];
       IF Inline.BITAND[i, 37B] = 0 THEN Process.Pause[1];
       ENDLOOP;
    SumLongCard[sendBuffer];
    DivideLongCardBuffer[bufferA];
    EchoCommand[stream, ComputeCal];
    SendBuffer[stream, 0, EikonixProtocol.Width, bufferA];
    END;
    
  cSelectClearFilter: 	CARDINAL = 8108H;
  cSelectRedFilter: 	CARDINAL = 8208H;
  cSelectGreenFilter: 	CARDINAL = 8308H;
  cSelectBlueFilter: 	CARDINAL = 8408H;
  cSelectOpaqueFilter:  CARDINAL = 8508H;
  
  SetFilter: PROC [stream: Stream.Handle] = BEGIN
    color: Color ← LOOPHOLE [Stream.GetWord[stream]];
    c: CARDINAL ← SELECT color FROM
      Red => cSelectRedFilter,
      Green => cSelectGreenFilter,
      Blue => cSelectBlueFilter,
      Opaque => cSelectOpaqueFilter,
      ENDCASE => cSelectClearFilter;
    BusyWait[];
    MBWrite[deviceCommandRegister, c];
    BusyWait[];
    EchoCommand[stream, SetFilter];
    END;
      
  SendSampledBuffer: PROC
    [stream: Stream.Handle, buffer: Address, bit8: BOOL] =  BEGIN
    w: WORD;
    cnt: CARDINAL = EikonixProtocol.Height/SampleLength;
    FOR j: CARDINAL IN [0 .. cnt) DO
      IF bit8
        THEN BEGIN
	  w ← MBRead[buffer + Inline.BITSHIFT[j, 3]];
	  Stream.PutChar[stream, Inline.HighByte[w]];
	  END
	ELSE BEGIN
	  w ← MBRead[buffer + Inline.BITSHIFT[j, 4]];
	  Stream.PutWord[stream, w];
	  END;
      ENDLOOP;
    Stream.SendNow[stream];
    END;
        
  SendMBBuffer: PROC
    [stream: Stream.Handle, start, count: CARDINAL, ib: Address, bit8: BOOL]
    = BEGIN
    bufferA.blockPointer↑ ← LOOPHOLE[Multibus.RawReadBlock[LOOPHOLE[ib]]];
--  DicentraInputOutput.ReadHyper
--    [to: @bufferA.blockPointer↑, from: LOOPHOLE[ib],
--    words: Multibus.wordsPerBlock];
    bufferA.startIndex ← start * (IF bit8 THEN 1 ELSE 2);
    bufferA.stopIndexPlusOne ← (start + count) * (IF bit8 THEN 1 ELSE 2);
    Stream.PutBlock[stream, LOOPHOLE[bufferA]];
    Stream.SendNow[stream];
    END;

 SendBuffer: PROC
    [stream: Stream.Handle, start, count: CARDINAL, buffer: Buffer] = BEGIN
    buffer.startIndex ← start * 2;
    buffer.stopIndexPlusOne ← (start + count) * 2;
    Stream.PutBlock[stream, LOOPHOLE[buffer]];
    Stream.SendNow[stream];
    buffer.blockPointer↑ ← ALL [0];
    END;
    
  SumScan: PROC [scanBuffer: Address, buffer: Buffer] = BEGIN
    FOR i: CARDINAL IN [0 .. EikonixProtocol.Width) DO
      buffer.blockPointer[i] ← buffer.blockPointer[i] +
        Inline.BITSHIFT[ReadEikonixBuffer[scanBuffer, i], -4];
      ENDLOOP;
    END;
  
  SumLongCard: PROC [scanBuffer: Address] = BEGIN
    FOR i: CARDINAL IN [0 .. EikonixProtocol.Width) DO
      longCardBuffer[i] ← longCardBuffer[i] + ReadEikonixBuffer[scanBuffer, i];
      ENDLOOP;
    Process.Yield[];
    END;
  
  DivideLongCardBuffer: PROC [buffer: Buffer] = BEGIN OPEN Inline;
    FOR i: CARDINAL IN [0 .. EikonixProtocol.Width) DO
      buffer.blockPointer[i] ← DBITAND[DBITSHIFT[longCardBuffer[i], - 9], 0FFFH];
      ENDLOOP;
    END;
    
  SetStagePosition: PROC [pos: CARDINAL] = BEGIN
    cmd: EikonixCommand;
    cmd.reg ← StagePosition;
    cmd.bits ← pos;
    MBWrite[deviceCommandRegister, LOOPHOLE [cmd]];
    END;
    
  StartScan: PROC [scanBuffer: Address, step: BOOL, bit8: BOOL] = BEGIN
    funct: EikonixFunctionControl;
    IF ~ step AND ~ bit8 THEN funct.funct ← Data12Bit;
    IF ~ step AND bit8 THEN funct.funct ← Data8Bit;
    IF step AND ~ bit8 THEN funct.funct ← Data12Step;
    IF step AND bit8 THEN funct.funct ← Data8Step;
    funct.direction ← 1;
    funct.buffer ← IF scanBuffer = dataBuffer0 THEN 0 ELSE 1;
    funct.go ← 1;
    MBWrite[functionControlRegister, LOOPHOLE [funct]];
    END;
    
  ReadEikonixBuffer: PROC
    [bufferAddress: Address, pixel: CARDINAL] RETURNS [CARDINAL] = INLINE BEGIN
    bufferAddress ← Inline.BITAND[pixel, 3777B] + bufferAddress;
    RETURN [MBRead[bufferAddress]];
    END;
    
  SendError: PROC [stream: Stream.Handle] =  BEGIN
    ERROR MyAbort;
    END;
  
  GetAddress: PROC [stream: Stream.Handle] RETURNS [EikonixProtocol.Address]
    = BEGIN
    md: MACHINE DEPENDENT RECORD [ a, b, c, d: CHAR ];
    md.a ← Stream.GetChar[stream];
    md.b ← Stream.GetChar[stream];
    md.c ← Stream.GetChar[stream];
    md.d ← Stream.GetChar[stream];
    RETURN [LOOPHOLE [md]];
    END;
  
  PutByteSwapWord: PROC [stream: Stream.Handle, word: WORD] = INLINE BEGIN
    Stream.PutWord[stream, ByteSwap[word]];
    RETURN;
    END;
    
  ByteSwap: PROC [in: CARDINAL] RETURNS [CARDINAL] = INLINE BEGIN
    RETURN [LOOPHOLE[Inline.BITROTATE[in, 8]]];
    END;
    
  MBWrite: PROC [reg: Address, data: WORD] = INLINE BEGIN
    data ← ByteSwap[data];
    DicentraInputOutput.RawWrite[data, LOOPHOLE [reg]];
    END;
  
  MBRead: PROC [reg: Address] RETURNS [WORD] =  INLINE BEGIN
    RETURN [ByteSwap[DicentraInputOutput.RawRead[LOOPHOLE [reg]]]];
    END;
    
  BusyWait: PROC [] =  BEGIN
    UNTIL (Inline.BITAND[MBRead[statusRegister], busyBit]) = 0 DO
--      Process.Yield[];
      ENDLOOP;
    END;
    
  DataWait: PROC [] = BEGIN
     UNTIL (Inline.BITAND[MBRead[functionControlRegister], goBit]) = 0 DO
--       Process.Yield[];
       ENDLOOP;
     END;

  EchoCommand: PROC [stream: Stream.Handle, cmd: EikonixProtocol.Command] = BEGIN
    stream.PutChar[LOOPHOLE[200B, CHAR]];
    stream.PutChar[LOOPHOLE[cmd, CHAR]];
    END;
  
  KillLights: PROC [] =  BEGIN
    DicentraInputOutput.Output[0, LOOPHOLE [MultibusAddresses.scc3 + 3B]];
    DicentraInputOutput.Output[0, LOOPHOLE [MultibusAddresses.scc3 + 5B]];
    END;
    
  New: PROCEDURE [words: LONG CARDINAL] RETURNS [p: LONG POINTER] =
    BEGIN
    pages: LONG CARDINAL ← Space.PagesFromWords[words];
    p ← Space.ScratchMap[pages];
    END;
  
  CheckScannerBusy: PROC [pupAddress: PupStream.PupAddress] = BEGIN
    IF active THEN ERROR PupStream.RejectThisRequest["Scanner busy"];
    END;
    
  Init[];
  
  END...