!**************
!*  BTT6S     *
!*DA:24.MAR.80*
!**************

%CONTROL K'100001';  ! 'SYSTEM' PROGRAM (FAST ROUTINE ENTRY/EXIT)
%PERMROUTINESPEC SVC(%INTEGER EP, P1, P2)
%BEGIN

     %SYSTEMROUTINESPEC MAPHWR(%INTEGER SEGS)
     %SYSTEMROUTINESPEC LINKIN(%INTEGER SER)

     %RECORDFORMAT PF(%BYTEINTEGER SERVICE, REPLY, %C
        %INTEGER A1, A2, A3)
     %RECORDFORMAT TTF(%INTEGER KBS, KBD, TTS, TTD)
     %RECORDFORMAT BUFF(%INTEGER PT, LAST, %BYTEINTEGERARRAYNAME B)
     %RECORDFORMAT BUFFX(%INTEGER PT, LAST, ARRAYPT)

     %CONSTRECORD (BUFFX) %NAME NULL = 0


     %CONSTINTEGER RUBOUT=K'177'
     %CONSTINTEGER CAN=24
     %CONSTINTEGER CR=13
     %CONSTINTEGER BELL=7
     %CONSTINTEGER ESC=K'33'
      %CONSTINTEGER SI=K'17';        ! SHIFT INTO LOWER MODE (CTRL O)
      %CONSTINTEGER SO=K'16';        ! SHIFT OUT (CTRL N)
      %CONSTINTEGER DLE=K'20';       ! (CTRL P)
      %CONSTINTEGER EOT = K'04';     ! EOF (CTRL D)
      %CONSTINTEGER DC1=K'21';       ! CANCEL OUTPUT (CTRL Q)
      %CONSTINTEGER TAB = 9;          ! TAB (IMPLEMENTED AS 3 SPACES)

     %OWNRECORD (TTF) %NAME TT=K'137560'
     %OWNINTEGER KBINT=-2
     %OWNINTEGER TTINT=-1
     %OWNINTEGER TTSER=1; ! ???
     %OWNINTEGER CLIID=2
     %OWNINTEGER TT STATUS=0, UPPER=32, TT IDLE=0, E PT=0, EFPT=0

     %RECORD (PF) P2
     %OWNRECORD (PF) %NAME P
     %CONSTINTEGER NO OF SPECS = 6

     %OWNBYTEINTEGERARRAY SPECS(0:NO OF SPECS) =
       RUBOUT, CAN, ESC, CR, SI, SO, TAB

     %INTEGER CHAR, I, IN MODE, E LAST
     %INTEGER OUTID, SEG, CLI FLAG, CID, CADR

     %OWNRECORD (BUFF)  OUT, INH
     %RECORD (BUFFX) %NAME BUFX, INX

      %RECORDFORMAT HF(%RECORD (HF) %NAME H, %RECORD (PF) P)
      %RECORDFORMAT QF(%RECORD (HF) %NAME H)

      %OWNRECORD (HF) %ARRAY HA(0:15)
      %OWNRECORD (HF) %NAME H
      %OWNRECORD (QF) HI, HO
      %OWNRECORD (QF) FREE

      %OWNINTEGER FIRST, LAST, CURR
      %OWNBYTEINTEGERARRAY BUFFER(0:255)

     %OWNBYTEINTEGERARRAY ECHOB(1:40)

     %SWITCH INS(0:NO OF SPECS), STATE(0:7)

     %ROUTINESPEC DRIVE TT(%INTEGER CHAR)
     %ROUTINESPEC ECHO(%INTEGER X)
     %ROUTINESPEC ECHO BELL
     %ROUTINESPFC TRANSFER INPUT
     %ROUTINESPEC OUTPUT REPLY
      %ROUTINESPEC PLANT(%INTEGER N)

!!     %CONSTBYTEINTEGERARRAY CANM(0:3)= 3, '#', CR, NL
!!     %CONSTBYTEINTEGERARRAY CLIM(0:3)= 3, '<', 8, '>'

     %CONSTINTEGER MYSEG=4, MSA=K'100000'
     %CONSTINTEGER MYISEG=3, MISA=K'060000'

     MAPHWR(5);        ! MAP REGS TO SEG 5
     LINKIN(TTSER); LINKIN(KBINT); LINKIN(TTINT)
     TT_KBS=K'100'
     BUFX==OUT
     INX==INH

     %CYCLE I = 15, -1, 0
        PUSH(FREE, HA(I))
     %REPEAT

     %CYCLE
        %IF OUTID=0 %AND %NOT HO_H == NULL  %START
         H == POP(HO); PUSH(FREE, H)
         P == H_P
        %ELSE
           P == P2
           P_SERVICE = 0
           POFF(P)
        %FINISH
        %IF P_SERVICE=KBINT&X'FF' %START
           CHAR=TT_KBD&127; ! STRIP PARITY BIT
           %CYCLE I=NO OF SPECS, -1, 0
              ->INS(I) %IF CHAR=SPECS(I)
           %REPEAT
           !! NORMAL CHAR
           %IF CHAR>='A'+K'40' %AND CHAR<='Z'+K'40' %THENC
             CHAR=CHAR-UPPER;       ! TURN TO UPPER
         PLANT(CHAR)
           %CONTINUE

INS(0):                     ! RUBOUT
          %IF LAST#CURR %START
               LAST = (LAST-1)&255
              ECHO('\')
           %ELSE ECHO BELL
           %CONTINUE
INS(1):    ! CANCEL
           %IF LAST#CURR %START
              LAST = CURR
              ECHO('#');  ECHO(CR);  ECHO(NL);  E LAST=E PT
           %ELSE ECHO BELL
           %CONTINUE

INS(2):         ! ESCAPE - GO TO CLI
            CLI FLAG = 1
               LAST = 0; CURR = 0; FIRST = 0
INS2:         ECHO('<');  ECHO(13);  ECHO('>')
           %CONTINUE

INS(4):             ! SHIFT IN
            UPPER = 0;  %CONTINUE

INS(5):             ! SHIFT OUT
             UPPER = 32;  %CONTINUE

INS(6):             ! TAB
             PLANT(' '); PLANT(' '); PLANT(' '); %CONTINUE

INS(3):              ! CR
            PLANT(NL); CURR = LAST
           TRANSFER INPUT
           E LAST=E PT;       ! ALLOW IT TO DO OUTPUT NOW

        %ELSE %IF P_SERVICE=TT INT&X'FF' %START
           ->STATE(TT STATUS)

DO OUT:
STATE(5):                       ! GOING IDLE
           TT STATUS=0
           %IF E PT>0 %THEN TT STATUS=2 %ELSESTART
              %IF OUT_LAST#0 %THEN TT STATUS=1
           %FINISH
           ->STATE(TT STATUS)

STATE(1):                      ! NORMAL OP
           CHAR=OUT_B(OUT_PT);  OUT_PT=OUT_PT+1
           %IF OUT_PT>=OUT_LAST %THEN TT STATUS=5 %AND OUTPUT REPLY
           DRIVE TT(CHAR)
STATE(0):  %CONTINUE

STATE(2):                       ! ECHO OP
           %IF EFPT<E PT %START
              EFPT=EFPT+1
              CHAR=ECHOB(EFPT)
              %IF EFPT=E LAST %THEN TT STATUS=5 %AND E LAST=-1
              DRIVE TT (CHAR)
           %ELSE
              TT STATUS = 6
              %IF EFPT=E LAST %START
                 E LAST=-1;  E PT=0;  EFPT=0
                 ->DO OUT
              %FINISH
           %FINISH
           %IF EFPT=E PT %THEN E PT=0 %AND EFPT=0
           %CONTINUE

STATE(3):                     ! NORMAL CR
STATE(4):                     ! ECHO CR
STATE(7):                     ! END OF LINE - NEWLINE
           TT STATUS=5
           DRIVE TT(NL+128)
           %CONTINUE

STATE(6):                  ! IN ECHO LINE
           %CYCLE; %REPEAT

        %ELSE %IF P_SERVICE=TT SER %START; ! USER REQUEST
           %IF P_A1=1 %START;        ! OUTPUT REQUEST
              %IF OUTID#0 %START
                 H == POP(FREE)
                 %IF H == NULL %START
REJ:                P_SERVICE= P_REPLY; P_REPLY = TT SER
                    P_A1 = 1; PON(P)
                    %CONTINUE
                 %FINISH
                 H_P = P;       ! COPY P INTO SAFE PLACE
                 PUSH(HO, H);   ! AND QUEUE IT
                 %CONTINUE
              %FINISH
              OUTID=P_REPLY
              SEG=P_A2>>13;         ! SEG NO OF BUFFER
              MAP VIRT(OUTID, SEG, MY SEG)
              BUFX_ARRAYPT=MSA+(P_A2&K'17777')
              OUT_PT=0;  OUT_LAST=P_A3;   ! LENGTH
              %IF OUT_LAST=0 %THEN OUTPUT REPLY %ELSESTART
                    ->DO OUT %IF TT STATUS=0;   ! TT IDLE
               %FINISH
           %ELSE
              !! INPUT REQUEST
              %IF P_A1 # 0 %START
                CID = P_REPLY;  CADR = P_A2
                %CONTINUE %IF P_A3 # 0; ! JUST READ FROM CLI
              %FINISH

              H == POP(FREE)
              -> REJ %IF H == NULL
              H_P = P;         ! COPY P INTO A SAFE PLACE
              PUSH(HI, H);     ! AND Q IT
               %IF P_A1#0 %AND FIRST=LAST %THEN -> INS2
               %IF FIRST#CURR %START;   ! NON EMPTY LINE
                  TRANSFER INPUT
              %FINISH
           %FINISH
        %FINISH
     %REPEAT

     %ROUTINE DRIVE TT(%INTEGER CHAR)
        %IF CHAR=NL %START
           TT STATUS=TT STATUS+2
           CHAR=CR
        %FINISH
        TT_TTD=CHAR
        TT_TTS=TT_TTS!K'100'; ! INTS ON
     %END

     %ROUTINE ECHO(%INTEGER X)
        %RETURN %IF E PT>40
        E PT=E PT+1;  ECHOB(E PT)=X
        %IF TT STATUS=0 %OR TT STATUS=6 %START
           TT STATUS=2
           DRIVE TT(X)
           EFPT=1
        %FINISH
     %END


     %ROUTINE ECHO BELL
        ECHO(BELL);  E LAST=E PT
     %END

     %ROUTINE PLANT(%INTEGER CHAR)
        BUFFER(LAST) = CHAR
        LAST = (LAST+1)&255
        ECHO(CHAR)
     %END

     %ROUTINE TRANSFER INPUT
        %INTEGER SEG, I, ID, ADR, N
        %IF CLI FLAG # 0 %START;  ! PREEMPTED BY CLI
           ID = CID;  ADR = C ADR; CLI FLAG = 0
        %ELSE
           %IF HI_H == NULL %THEN %RETURN
           H == POP(HI);  PUSH(FREE, H)
           ID = H_P_REPLY; ADR = H_P_A2
        %FINISH

        %IF ID#0 %START
           SEG=ADR>>13
           MAP VIRT(ID, SEG, MYISEG)
           INX_ARRAY PT=MISA+(ADR&K'17777')
           %CYCLE I = 0, 1, 80
               N = BUFFER(FIRST)
               INH_B(I) = N
               FIRST = (FIRST+1)&255
               %EXIT %IF N = NL
           %REPEAT
           P_SERVICE=ID;  P_REPLY=TTSER
           P_A1=I+1
           PON(P)
           MAP VIRT(0, -1, MYISEG)
        %FINISH
     %END

     %ROUTINE OUTPUT REPLY
        MAP VIRT(0, -1, MYSEG)
        P_SERVICE=OUTID;  P_REPLY=TTSER
        P_A1=0
        PON(P)
        OUTID=0; OUT_LAST = 0
     %END

%ENDOFPROGRAM