!**************
!*  BVT1S     *
!*DA:09.APR.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
     ROUTINESPEC  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(8); ECHO(' '); ECHO(8)
           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 BELL
           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