-- RS232ServerImpl.mesa
-- Copyright (C) 1984, 1985, 1987 Xerox Corporation.  All rights reserved.
-- Tim Diebert:     15-Jun-87  8:48:44
  
DIRECTORY
  DicentraInputOutput,
  DicentraRS232CAsync,
  MultibusAddresses,
  Process,
  PupDefs USING [PupAddressLookup, PupNameTrouble],
  PupStream USING [CloseReason, CreatePupByteStreamListener, PupAddress,
     PupListener, RejectThisRequest, SecondsToTocks, StreamClosing],
  Stream,
  String USING [Copy],
  TTY;
  
RS232ServerImpl: MONITOR
  IMPORTS DicentraInputOutput, DicentraRS232CAsync, Process, PupDefs, PupStream, Stream, String =
  BEGIN
  
  rs232Stream: Stream.Handle ← NIL;
  listener: PupStream.PupListener ← NIL;
  active: BOOL ← FALSE;
  kill, killKeyWatcher: BOOL ← FALSE;
  currentUser: LONG STRING ← [256];
  wakeUp: CONDITION; -- set their timeouts
  maxBuffer: CARDINAL ← 2048;
  buffer: ARRAY [0 .. 2048) OF CHAR;
  bufferPtr: CARDINAL ← 0;
  almostFull: CARDINAL ← 2000;
    
  KeyWatcher: PROC = { -- watches the input stream from the RS232 port
    PutIt: ENTRY PROC [c: CHAR] = {
      IF bufferPtr > almostFull THEN DO NOTIFY wakeUp; EXIT; ENDLOOP;
      IF bufferPtr <= maxBuffer-1 THEN
         {buffer[bufferPtr] ← c; bufferPtr ← (bufferPtr + 1) MOD 256f };
      }; -- PutIt
      
    DO
      c: CHAR ← Stream.GetChar[rs232Stream];
      PutIt[c];
      ENDLOOP;
    }; -- KeyWatcher
    
    
  keySenderRunning: BOOL ← FALSE; 
   
  KeySender: PROC [s: Stream.Handle] = {
    ENABLE {PupStream.StreamClosing => GOTO Out; UNWIND => NULL};
    UpdateBuffer: ENTRY PROC = {ENABLE UNWIND => NULL;
       FOR i: CARDINAL IN [0 .. bufferPtr) DO Stream.PutChar[s, buffer[i]] ENDLOOP;
       bufferPtr ← 0;
       }; -- UpdateBuffer
    BufferCheck: ENTRY PROC RETURNS [BOOL] = {ENABLE UNWIND => NULL;
       RETURN[bufferPtr = 0]}; -- BufferCheck
    Wait: ENTRY PROC = {ENABLE UNWIND => NULL; WAIT wakeUp}; 
      
    keySenderRunning ← TRUE;
    DO -- until told to die
      Wait[]; -- timeout or a notify by KeyWatcher
      IF kill THEN GOTO Out; -- die........
      IF BufferCheck[] THEN LOOP; -- nothing to do
      UpdateBuffer[];
      Stream.SendNow[s];
      ENDLOOP; -- until told to die 
    EXITS Out => {kill ← FALSE; keySenderRunning ← FALSE; RETURN}; -- and kill process
    };
  
  NetWatcher: PROC [s: Stream.Handle] = {
    ENABLE PupStream.StreamClosing => {kill ← TRUE; Note[]; GOTO Out};
    DO
      c: CHAR ← Stream.GetChar[s];
      Stream.PutChar[rs232Stream, c];
      ENDLOOP;
    EXITS Out => {Stream.Delete[s]; RETURN};
    };
    
  Note: ENTRY PROC [] = {ENABLE UNWIND => NULL; NOTIFY wakeUp};

  InitCommand: PROC [stream: Stream.Handle, pupAddress: PupStream.PupAddress] =
    BEGIN ENABLE {
      PupStream.StreamClosing => GOTO Exit;
      Stream.TimeOut => GOTO Exit};
    port: DicentraRS232CAsync.PortNumber ← LOOPHOLE[Stream.GetChar[stream]];
    charLength: DicentraRS232CAsync.CharLength ← LOOPHOLE[Stream.GetChar[stream]];
    speed: DicentraRS232CAsync.LineSpeed ← LOOPHOLE[Stream.GetChar[stream]];
    parity: DicentraRS232CAsync.Parity ← LOOPHOLE[Stream.GetChar[stream]];
    stop: DicentraRS232CAsync.StopBits ← LOOPHOLE[Stream.GetChar[stream]];
    currentUser.length ← 0;
    PupDefs.PupAddressLookup[currentUser, pupAddress
      !  PupDefs.PupNameTrouble => {String.Copy[to: currentUser, from: e]; CONTINUE}];
    bufferPtr ← 0;
    active ← TRUE;
    Stream.PutChar[stream, 'k];
    Stream.SendNow[stream];
    bufferPtr ← 0;
    Process.Detach[FORK KeySender[stream]];
    BEGIN ENABLE PupStream.StreamClosing, Stream.TimeOut =>
      {IF keySenderRunning THEN kill ← TRUE; Note[]; GOTO Out};
      DO
        c: CHAR ← Stream.GetChar[stream];
        Stream.PutChar[rs232Stream, c];
        ENDLOOP;
      EXITS Out => {Stream.Delete[stream]; GOTO Exit};
      END;
    EXITS Exit => {active ← FALSE; RETURN};
    END;
    
  CheckBusy: PROC [pupAddress: PupStream.PupAddress] = BEGIN
    IF active THEN ERROR PupStream.RejectThisRequest[currentUser];
    END;
  
  Init: PROC [] = BEGIN
    ttyImpl, backing: Stream.Handle;
    name: STRING = "DicentraRS232.log"L;
    bufferPtr ← 0;
    [ttyImpl, backing] ← DicentraRS232CAsync.CreateRS232Instance[name, NIL,
      TTY.nullHandle, -- [charLength, speed, parity, stop] --];
    rs232Stream ← ttyImpl;
    IF listener = NIL THEN listener ← PupStream.CreatePupByteStreamListener[
      local: [0, 32B], 
      proc: InitCommand,
      ticks: PupStream.SecondsToTocks[30*60],
      filter: CheckBusy];
    SetBaudRate[]; -- wired to 300
    Process.SetTimeout[@wakeUp, Process.MsecToTicks[2000]];
    Process.Detach[FORK KeyWatcher[]];
    END;
  
  
  -- Krock: Use second connector because it doesn't fit.
  scc0: DicentraInputOutput.IOAddress = MultibusAddresses.scc2;
  chanB: LONG POINTER TO Words = scc0 + 00H;
  chanA: LONG POINTER TO Words = scc0 + 10H;
  chan: LONG POINTER TO Words ← chanA;  -- Line 0
  Words: TYPE = RECORD [
    r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15: WORD];

  SetBaudRate: PROC [] = {
     DicentraInputOutput.Output[000H, @chan.r14];  -- Disable Baud Rate Gen from PClk
     DicentraInputOutput.Output[002H, @chan.r12];  -- Low byte of time constant
     DicentraInputOutput.Output[001H, @chan.r13];  -- High byte of time constant
     DicentraInputOutput.Output[003H, @chan.r14];  -- Enable Baud Rate Gen from PClk
     };
     
  Init[];
  
  END.