! Terminal handler process

! Input is inserted by the interrupt handler into a type-ahead buffer
! belonging to this process, the interrupt handler deals with:
! ^S suspend output (XOFF)
! ^Q resume output (XON)
! ^O toggle discard mode
! ^P exempt next character from control significance
! ^X discard type-ahead
! ^T reboot if followed by R
! ^Y clobber last input client (needs tidying up)

! The main process deals with keyboard input request messages.  On every
! request the entire contents of the type-ahead buffer are copied into
! the message buffer, which is returned to the user, a message size of
! 0 indicates that there was no type-ahead.  This method is somewhat crude,
! in that it requires a message interchange for every call on TESTSYMBOL,
! so if input is being expected, messages will be flying around all the time.
! It works, but should be improved when time permits.

! Output is inserted into the buffer by routine SCREENPUT,
! and removed from the buffer by the interrupt handler.
! SCREENPUT is a nasty at-routine (16_3FA2) callable by any process.

%option "-low-nons-nocheck-nodiag"
%include "mouse.inc"

%constinteger input buffer size = 300
%constinteger output buffer size = 2000
%constinteger ion=16_B1,iof=16_91

%ownrecord (interrupt handler fm) ih
%ownrecord (semaphore fm)%name output buffer full
%ownrecord (mailbox fm)%name service mailbox, reply
%ownrecord (message fm)%name request == nil
%ownrecord (pcb fm)%name last customer == nil
%ownbytearray input buffer(1:input buffer size)
%ownbytearray output buffer(1:output buffer size)
%ownbytename in start, in limit, in put, in get
%ownbytename out start, out limit, out put, out get
%owninteger breaking=0,discard=0,xoff=0,dleing=0

@16_3724 %integer exemptmask
@16_3f94 %integer ttygla
@16_3fa0 %byte ttyrows,ttycols,%short ttyjmp,%integer screenputad
@16_4000c1 %byte acias,*,aciad

%begin
%label int

  %routine keyboard put(%register(d0)%integer sym)
! Add character to input buffer (called from interrupt handler)
  %register(a0)%bytename new
    *temp a1
    input = sym
    new == input[1]
    new == instart %if new==inlimit  {wrap round}
    input == new %unless new==inget  {do not over-fill}
  %end

  %integerfn output pending
! Number of bytes waiting in output buffer
  %register(d1)%integer a
    *temp d0
    a = addr(output)-addr(outget)
    a = a+output buffer size %if a<0
    %result = a
  %end

  %routine screenput(%integer x)
! Add character to output buffer, wait if nearly full.
  %bytename new
  %integer olda4=a4
    a4=ttygla
    %if discard=0 %start
      output = x; new == output[1]
      new == outstart %if new==outlimit
      output == new
      acias = ion
      semaphorewait(output buffer full) %if outputpending>outputbuffersize-5
    %finish
    a4 = olda4
  %end

  %routine setup interrupts(%integer pc)
  %integer x
    ih = 0
    ih_pc = pc
    x = or to sr(16_2000)
    add interrupt handler(5)
    x = move to sr(x)
  %end

  become process(2000)
  ttygla = a4
  exemptmask = 0
  output buffer full == create semaphore(0)
  service mailbox == create mailbox(create semaphore(0))
  define kernel object(service mailbox,"Keyboard mailbox")
  in start == input buffer(1)
  in limit == in start [input buffer size]
  in put == in start
  in get == in start
  out start == output buffer(1)
  out limit == out start [output buffer size]
  out put == out start
  out get == out start
  setup interrupts(addr(int))
  ttyrows = 24; ttycols = 80; ttyjmp = 16_4ef9
  *lea screenput,a0; screenputad = a0
  acias = ion
  %cycle
    request == receivemessage(service mailbox)
    last customer == request_sender
    reply == request_reply
    request_size = 0
    %while inget##input %cycle
      request_size = request_size+1
      request_data(request_size) = inget
      inget == inget[1]
      inget == instart %if inget==inlimit
    %repeat
    send message(request,request_reply)
    discard = 0
  %repeat

int: *temp d0-d1/a0-a1
%register(d1)%integer sym

  sym = acias
  %if sym&16#0 %start  {framing error => break}
    sym = aciad
    acias = ion
    breaking = 1
! %elseif sym&4#0      {DCD (DTR) gone}
!   sym = aciad
!   acias = ion
  %elseif sym&1#0      {input ready}
    sym = aciad&127
    %if breaking#0 %start
      breaking = 0
      acias = ion
    %elseif dleing#0
      dleing = 0
    %elseif sym<32     {control character} %and 1<<sym&exemptmask=0
      %if sym='T'-64 %start
        sym = acias&1 %until sym#0; sym = aciad&127
        %if sym&95='R' %start
          *move.w #16_2700,d0; *trap #0
          *move.l 0,sp; *move.l 4,-(sp); *rts
        %finish
      %finish
      %if sym='P'-64 %start
        dleing = 1
      %elseif sym='X'-64
        input == inget
      %elseif sym='S'-64
        xoff = 1; sym = -1
      %elseif sym='Q'-64
        xoff = 0; acias = ion; sym = -1
      %elseif sym='O'-64
        discard = discard!!1; sym = -1
      %elseif sym='Y'-64
        last customer_time slice left = -1 %unless last customer==nil
        sym = -1
      %finish
    %finish
    keyboard put(sym) %unless sym<0
  %elseif sym&2#0           {OUTPUT READY}
{}sym = aciad %if sym&4#0 {DTR gone}
    outget == output %if discard#0
    %if output==outget %or xoff#0 %start
      acias = iof
    %elseif breaking=0
      aciad = outget
      outget == outget[1]
      outget == outstart %if outget==outlimit
      %if output pending < output buffer size>>2 -
      %and output buffer full_counter<0 %start
        int signal semaphore(output buffer full)
      %finish
    %finish
  %finish

  returnfrominterrupt
  *nop
%end
