! prep options
! k - kent (uses bigger small buffers)
! t - put task id in mode field and use 128 as flag for small buffs
!
! FILE 'FEP_BUFF3'
!*******************************
!* EMAS-2900   BUFFER MANAGER *
!*     FILE: BUFF3 (FEP)      *
!*     DATE: 29.JAN.81        *
!******************************

!! STK = SIZE+200
CONSTINTEGER  SIZE = K'33000';   ! WAS 35000, BUT NEEDS LINK6S
CONTROL  K'100001'
SYSTEMROUTINESPEC  LINKIN(INTEGER  SER)
SYSTEMROUTINESPEC  ALARM(INTEGER  TICKS)
RECORDFORMAT  D(INTEGER  I)
CONSTRECORD  (D) NAME  NULL = 0


BEGIN 

     RECORDFORMAT  BF(RECORD  (BF) NAME  L, BYTEINTEGER  LEN, MODE, C 
       BYTEINTEGERARRAY  A(0:99))

     RECORDFORMAT  QF(RECORD  (BF) NAME  L)
     RECORDFORMAT  R1F(INTEGER  X)
     RECORDFORMAT  R3F(BYTEINTEGERNAME  X)
     RECORDFORMAT  PE(BYTEINTEGER  SER, REPLY, FN, PORT, C 
       RECORD  (BF) NAME  MES, BYTEINTEGER  LEN, S1)

     RECORDFORMAT  P2F(BYTEINTEGER  SER, REPLY, INTEGER  A, B, C)

     CONSTBYTEINTEGERNAME  OWN ID = K'160030'
     CONSTINTEGER  REQUEST BUFFER = 0
     CONSTINTEGER  RELEASE BUFFER = 1

     CONSTBYTEINTEGERNAME  INT = K'160060'

    CONSTINTEGERNAME  PS = K'017776';    ! PDP11 PROCESSOR STATUS WORD (SEG 0)

     CONSTBYTEINTEGERNAME  CHANGE OUT ZERO = K'160310'
     CONSTINTEGER  T3 SER = 21

     CONSTINTEGER  SER NO = 17


     OWNRECORD  (QF) NAME  FREE BIG
     OWNRECORD  (QF) NAME  FREE SMALL

#if k
     CONSTINTEGER  NO OF BIG = 31, BIG L = 256
     CONSTINTEGER  NO OF SMALL = 45, SMALL L = 128
#else
     CONSTINTEGER  NO OF BIG = 34, BIG L = 256
     CONSTINTEGER  NO OF SMALL = 78, SMALL L = 64
#fi


     !* NOTE: FOR ONE SEGMENT (ELSE CHANGE ABOVE)
     !!        4*SMALL = BIG     TOTAL = (N SMALL+1)*4+N BIG = 32
     CONSTINTEGER  QL = 127;            ! SIZE OF 'REQUEST' QUEUE
     OWNINTEGER  QUEUED = 0
     OWNINTEGER  NB = 0, NS = 0, QQ = 0, LB = 999, LS = 999
           ! NB -> K'100112' (IN OTHER VMS)
     OWNINTEGER  BR = 0,  SR = 0
     OWNINTEGER  DELAY = 60;       ! 60 MINS
      OWNINTEGER  DCOU

     OWNINTEGERARRAY  MONIT(0:20)
     OWNRECORD  (PE) ARRAY  PA(0:QL)

     SYSTEMROUTINESPEC  MAP HWR(INTEGER  SEG); ! WARNING: ADDS A WORD TO GLA


     INTEGER  I, ADD, PT, LEN, TOP, BOT, MID PT

     OWNRECORD  (PE)P
     OWNRECORD  (P2F) NAME  P2
     OWNRECORD  (BF) NAME  B
     OWNRECORD  (R1F)R1
     OWNRECORD  (QF) NAME  R2
     OWNRECORD  (R3F) NAME  R3


     BYTEINTEGERARRAY  BUFF(0:SIZE)

     ROUTINESPEC  QUEUE(RECORD  (PE) NAME  P)
     INTEGERFNSPEC  UNQUEUE(INTEGER  LEN)
     ROUTINESPEC  CRASH(INTEGER  I)


     LINKIN(SER NO)
     MAP HWR(0);                     ! MAP H/W REGS TO MY SEG 0
     CHANGE OUT ZERO = T3 SER;       ! POINT OUTPUT(0) TO COMMON OUT
     ALARM(60*50);                    ! ONE MINUTE
     P2 == P
     R2 == R1;  R3 == R1

     R3_X == BUFF(0)
     TOP = R1_X
     R3_X == BUFF(SIZE)
     BOT = R1_X
     ! OCTAL(TOP); ! OCTAL(BOT); ! NEWLINE
      PT = (TOP&K'17700')+K'100'
      PT = PT!(TOP&K'160000')
      ! OCTAL(PT)

     CYCLE  I = 1, 1, NO OF SMALL
        R1_X = PT;  PT = PT+SMALL L
        B == R2_L
        B_L == FREE SMALL
        FREE SMALL == B
#if t
        B_MODE=128+SER NO;   !my buffer
#else
        B_MODE = 64 + 1;    !NOT IN USE BIT
#fi
         NS = NS+1
     REPEAT 

     MID PT = PT;        !address of start of big blocks
     CYCLE  I = 1, 1, NO OF BIG
        R1_X = PT;  PT = PT+BIG L
         B == R2_L
        B_L == FREE BIG
        FREE BIG == B
#if t
        B_MODE=SER NO;  !my buffer
#else
        B_MODE = 1;     !'FREE' BUFFER BIT
#fi
         NB = NB+1
     REPEAT 
     ! OCTAL(PT); !  NEWLINE

#if k
#if t
      PRINTSTRING("Buff5(kt)")
#else
      PRINTSTRING("Buff5(k)")
#fi
#else
#if t
      PRINTSTRING("Buff5(t)")
#else
      PRINTSTRING("Buff5")
#fi
#fi
      NEWLINE
     CYCLE 
        P_SER = 0;  POFF(P2)

         IF  P_REPLY = 0 START ;      ! CLOCK TICK
            IF  '0'<=INT<='9' THEN  DELAY =INT-'0' AND  C 
               INT = 'P' AND  DCOU = 0
            ALARM(50*60)

            IF  NB = 0 START 
              PRINTSTRING("BUFF: NO BIG BUFFERS ******
")
           FINISH 
           IF  NS = 0 THEN  PRINTSTRING("BUFF: NO SMALL BUFFERS ******
")
           DCOU = DCOU+1
            IF  DCOU = DELAY OR  INT = '?' START 
               INT = 0
               DCOU = 0
               PRINTSTRING('        BUFF:')
               WRITE(NB, 1); WRITE(NS, 1)
               WRITE(LB, 1); WRITE(LS, 1)
               WRITE(BR, 3); WRITE(SR, 1)
               WRITE(QUEUED, 3); WRITE(QQ, 1); NEWLINE
                QQ = 0;  LB = 999; LS = 999; BR = 0;  SR = 0
            FINISH 
            CONTINUE 
          FINISH 

        IF  P_FN = REQUEST BUFFER START 
AGAIN:                                 ! COMES HERE IF IT WAS A
                                       ! QUEUED REQUEST
           IF  P_LEN = 0 START ;       ! BIG BUFFER
              PS = PS!K'340';         ! ENSURE NO INTERRUPTS HERE
              UNLESS  FREE BIG == NULL START 
                 P_MES == FREE BIG;  FREE BIG == P_MES_L
                 NB = NB-1;  IF  NB < LB THEN  LB = NB
                 BR = BR+1
                 PS = PS&(¬K'340');   ! INTS ON AGAIN

                 IF  P2_B < MID PT THEN  CRASH(1);  !in small buffer area
                 -> REPLY
              FINISH 
              PS = PS&(¬K'340')
              QUEUE(P2)
           ELSE 
              !! SMALL BLOCK REQUEST
              UNLESS  FREE SMALL == NULL START 
                 P_MES == FREE SMALL;  FREE SMALL == P_MES_L
                 NS = NS-1; IF  NS < LS THEN  LS = NS
              SR = SR+1
                 IF  P2_B >= MID PT THEN  CRASH(2)
REPLY:           
                 P_SER = P_REPLY;  P_REPLY = SER NO
                 P_MES_L == NULL
#if t
                 P_MES_MODE=P_MES_MODE & 128 + P_SER; !new owner
#else
                 P_MES_A(1) = P_SER;      ! PUT WHO TO IN IT
                 IF  P_MES_MODE & 1 = 0 THEN  CRASH(5);  !NOT FREE
                 P_MES_MODE = P_MES_MODE - 1;   !REMOVE 'FREE' BIT
#fi
                 P2_B = P2_B-K'20000'
                    !! PUT BLOCK ADDRESS IN SEG 4/5

                 PON(P2)
              ELSE 
                 QUEUE(P2)
              FINISH 
           FINISH 
           CONTINUE 
        FINISH 

        !! SHOULD BE RELEASE BUFFER
        IF  P_FN = RELEASE BUFFER START 

           P2_B = P2_B+K'20000';     ! BLOCK ADDRESS IN SEG 5/6

           IF  P_MES_MODE & 1 # 0 THEN  CRASH(6);   !ALREADY FREE
           P_MES_MODE = P_MES_MODE ! 1
#if t
           IF  P_MES_MODE & 128 = 0 START 
#else
           IF  P_MES_MODE & K'376'= 0 START 
#fi
              IF  P2_B < MID PT THEN  CRASH(3)
              PS = PS!K'340';        ! NO INTERRUPS IN THIS SECTION
              P_MES_L == FREE BIG
              FREE BIG == P_MES
              NB = NB+1
              PS = PS&(¬K'340');     ! INTS ON AGAIN HERE
              LEN = 0;         ! BIG BLOCK
           ELSE 
              IF  P2_B >= MID PT THEN  CRASH(4)
              P_MES_L == FREE SMALL
              FREE SMALL == P_MES
              LEN = 1;                 ! SMALL BLOCK
              NS = NS+1
           FINISH 

           !! CHECK FOR A QUEUED REQUEST
           IF  QUEUED > 0 START 
              IF  UN QUEUE(LEN) # 0 THEN  -> AGAIN
              !! # 0 -> FOUND A REQUEST, WHICH IS COPIED TO "P"

           FINISH 
        FINISH 
     REPEAT 


     ROUTINE  QUEUE(RECORD  (PE) NAME  P)
        INTEGER  I
        RECORD  (PE) NAME  P2

        CYCLE  I = 0, 1, QL
           P2 == PA(I)
           IF  P2_SER = 0 START ;      ! QUEUE SLOT NOT ALLOCATED
              P2 = P;                  ! COPY P INTO PA
              QUEUED = QUEUED+1;  QQ = QQ+1
              RETURN 
           FINISH 
        REPEAT 
        PRINTSTRING("FULL!
        ")
     END 


     INTEGERFN  UN QUEUE(INTEGER  LEN)
        INTEGER  I, OLD
        RECORD  (PE) NAME  P2
        OWNINTEGER  IN TURN

        OLD = IN TURN
        CYCLE 
           P2 == PA(IN TURN);  IN TURN = (IN TURN+1)&QL
           IF  P2_SER # 0 AND  P2_LEN = LEN START 
              P = P2;                  ! COPY PA INTO P
              P2_SER = 0;              ! SLOT NOW FREE
              QUEUED = QUEUED-1
              RESULT  = 1
           FINISH 
           IF  IN TURN = OLD THENEXIT 
        REPEAT 
        RESULT  = 0
     END 

     ROUTINE  CRASH(INTEGER  I)
         PRINTSTRING("Buff: small/large buffer error, task:")
         WRITE(P_REPLY, -1);  PRINTSTRING(" code:")
         WRITE(I,-1);  NEWLINE
         CYCLE ; REPEAT 
     END 
ENDOFPROGRAM