!******************************
!*  file system handler       *
!*   fsys1s/fsys1y            *
!*  date: 30.Jun.81           *
!******************************

!*w.s.c. 25th august 1976
!*b.g.  27.mar.78

!*this handler is the file system utility to replace the
!*existing one in deimos to permit a file system to be
!*created on the ampex 9500 disc as well as the rk05's.

!*it is a conceptual copy of the rk05 file system handler
!*except that a buffer pool is used for block descriptors
!*and directory blocks.

!*the code is shared by 3 system slots,4 for the rk05's,
!*and 9,15 for the ampex disc.the ampex disc is logically
!*divided into two,units 2&3.
!* a further disc is catered for in slot 28

!*the clock is used to write blocks back every 10secs
!*(block descriptor blocks).directory blocks are always
!*written back as soon as possible after a change.

!*tuneable parameters

!*     nbuf=number of buffers in pool-1(must be>0)

!*     secs::length of time between inspecting buffer
!*          pool for writing back to disc.

!*the following facilities are offered

!*     examine a file
!*     get next block of a file
!*     destroy a file
!*     create a file
!*     append a block to a file
!*     rename a file
!*     rename a temporary file

!*stack=300     streams=0

!**********************************************************
!**********************************************************

%control x'4001'
%include "b_deimosspecs"

%begin

     !*********************************************************
     !*************     data areas &declarations     **********
     !*********************************************************


     !*system slots/disc

     %constinteger max drives = 4

     %constbyteintegerarray serv(0:max drives) = 3, 3, 8, 14, 28
     %constbytearray myser(0:max drives) = 4, 4, 9, 15, 29

     !*directory block areas/disc
     %ownintegerarray dirblk(0:max drives)

     !*block descriptor base/disc
     %ownbyteintegerarray blklst(0:max drives)

     !*free block start/disc
     %ownintegerarray fblock(0:max drives)

     %ownintegerarray first free(0:max drives)

     !*top of disc
     %ownintegerarray lastbl(0:max drives)

     !*request types

     %constinteger examine = 0
     %constinteger get next = 1
     %constinteger destroy = 2
     %constinteger create = 3
     %constinteger append = 4
     %constinteger rename = 5
     %constinteger rename temp = 6
     %constinteger rename fsys = 7
     %constinteger dir blk no = 8
     %constinteger report unit = 10
     %constinteger report unit2 = 11

     !*system constants

     %constinteger dread = 0, dwrite = 1
                                       !modes
     %constinteger clock int = 0
     %constinteger my seg = 4, msa = k'100000'

     !*system slots

     %constinteger rkser = 4
     %constinteger amp1ser = 9
     %constinteger amp2ser = 15
     %constinteger rkbser = 29

     %switch request(0:dir blk no)

     %integer id, seg, i, bk, no, nosave, pr, exit, seg2
     %owninteger drive, fno

     !*message formats

     %recordformat pf(%byteinteger service, reply, (%integer a,  %c
     (%integer b %or %integername xa2), %integer c %or %c
      %byte a1, a2, b1, b2, c1, c2))

     %record (pf)p, px

     !*disc buffer pool

     %constinteger secs = 5;           !buffer write back time
     %constinteger nbuf = 3;           !number of buffers-1(must be>0)

     %recordformat xf(%integer x)
     %recordformat bf(%integer drive, block, wrm, %record (xf) %c
       %array blk(0:255))
     !*wrm is a write marker to say that block has been
     !*altered and must be written back to disc.
     %ownrecord (bf) %array b(0:nbuf)
     %owninteger blast = 0;            !last buffer used in pool
     %ownrecord (bf) %name bx;         !points to current buffer record

    %ownintegerarray dum(0:20);         ! compiler fault in GLA length

     !*formats for block descriptors and directory blocks

     %recordformat blkf(%integer pr, next)
                                       !block descriptor

     %recordformat n1f((%byteintegerarray name(0:5) %or %integer a, b, c))
                                       ! two forms of the file name

     %recordformat inff(%byteinteger unit, fsys, %record (n1f)n)
                                       ! file descriptor

     %recordformat filef(%record (n1f)n, %integer first, pr)
                                       !directory entry

     %ownrecord (blkf) %arrayname blka
     %record (filef) %arrayname fa
     %ownrecord (filef) %name f
     %record (blkf) %name blk
     %record (blkf)save blk
     %record (inff) %name inf, inf2
     %record (inff)g

     !***********************************************
     !* e v e n t s 
    
       %on %event 15 %start;        ! disc i/o fail
         %if px_service = 0 %then -> restart; ! in timer section
         -> reply
      %finish

     !**********************************************

     !****************************************************************
     !******************************************************************

     !*routine da

     !*calls disc handler to read in a block
     !* nb:  this routine assumes that bx points to the block descriptor


     %routine da(%integer mode)
        %record (pf)p

        p_c = bx_block;               ! compiler error forces this
        p_service = serv(bx_drive)
        p_reply = id
        %if bx_drive = 1 %then p_c = p_c!k'020000'
        p_a = mode
        %if mode # d read %then bx_wrm = 0
                                       ! clear the write marker
        p_xa2 == bx_blk(0)_x
        ponoff(p)
        %if p_a # 0 %thensignal 15, 15
     %end

     !*******************************************************

     !*record map load

     !*loads requested block into core if it is not already there
     !*and returns a pointer to the start of the record bx
     !*which is set up to current entry in the buffer pool
     !*drive is assumed to be set up.   ********
     !* the routine also sets up global bx as a side effect

     %record (bf) %map load(%integer block)
        %integer i, temp

        !*check if block already in pool

        %cycle i = nbuf, -1, 0
           bx == b(i)
           %if bx_drive = drive %and bx_block = block %start
              %result == bx
           %finish
        %repeat

        !*block not in pool

        bx == b(blast)
        blast = blast+1
        %if blast > nbuf %then blast = 0
        %if bx_wrm # 0 %start;         !write back old block
           da(dwrite)
        %finish
        bx_drive = drive
        bx_block = block
        da(dread);                     !read in new block
        %result == bx
     %end

     !************************************************************


     !*record map exam 

     !*to read in correct directory block
     !*and find required entry

     %record (filef) %map exam(%record (inff) %name inf)
        %integer n, j, k, hit, t

        %record (n1f) %name file
        %record (n1f) %name info

        %record (filef) %name f

        !*set up drive number,0,1 rk05
                                       !2,3 ampex

        drive = inf_unit
        info == inf_n;                 ! point to name part

        !*set up directory block for scan

        t = dirblk(drive)
        n = t+inf_fsys;                ! map to users directory
        %cycle;         ! system occupies 3 blocks
           fa == load(n)_blk

           !*look for match

           %cycle j = 0, 1, 50
              fno = j;                 ! global for create
              f == fa(j);              ! point to target entry
              %if f_n_a = info_a %and f_n_b = info_b %and f_n_c = %c
                info_c %thenresult == f
           %repeat
           n = n+1
        %repeat %until n > t+2
        %result == null
     %end

     !******************************************************************

     !*record map get block

     !*returns pointer to correct block descriptor
     !*after calling load to read it into core

     %record (blkf) %map get block(%integer block no)
        blka == load(block no >> 7+blklst(drive))_blk
                                       !block desc block
        %result == blka(block no&k'177')
                                       ! offset into block
     %end

     !**********************************************************

     !*integer function appendb

     !*returns next free block number


     %integerfn appendb(%integer last)
        %integer wrap

        wrap = 0
        %cycle
           last = last+1
           %if last = lastbl(drive) %start
              %if wrap = 1 %thenresult = 0
              wrap = wrap+1
              last = fblock(drive)
           %finish
           blk == get block(last)
           %if blk_pr = 0 %thenresult = last
        %repeat
     %end

      %routine rewrite dir
         %integer i
         %cycle i = nbuf, -1, 0
            %if b(i)_wrm # 0 %start
               bx == b(i)
               da(dwrite)
            %finish
         %repeat
      %end

      %routine do report unit(%integer type)
         %integer i, j

         i = p_a2
         %if serv(i) # p_reply %then %return;   ! enforce a check

         %if type = report unit %start
            fblock(i) = p_b; first free(i) = p_b
            lastbl(i) = p_c
         %finish %else %start
            dirblk(i) = p_b
            blklst(i) = p_c
            linkin(myser(i))
         %finish
      %end


     !*****************************************************************

     !*************************************************************
     !*************************************************************


     !*main control loop

     !*link to system slots
     linkin(rkser)
     id = getid
     alarm(secs*50);                   !set clock for secs seconds

restart:
     %cycle
        p_service = 0
        poff(p)

        !*if clock tick check if buffer pool needs writing

        %if p_reply = clock int %start

           alarm(secs*50)
           px_service = 0;           ! for event 15 handling

           rewrite dir
           %continue
        %finish

        !*not a clock tick--request for service

        %if report unit <= p_a1 <= report unit 2 %then %c
          do report unit(p_a1) %and %continue

        px_service = p_reply
        px_reply = p_service
        px_b = p_b

        !*get callers block

         no = 0

        seg = p_b >> 13
        %if seg = 0 %then -> reply;    ! reject it
        i = map virt(p_reply, seg, my seg)
        %if i = 0 %then -> reply
        inf == record(msa+(p_b&k'17777'));  inf2 == inf
        %if dirblk(inf_unit) = 0 %then -> reply;  ! disc not present

        -> request(p_a)

        !*
        !**
        !***** examine file
        !**
        !*

request(examine):

        !*p_b has address of descriptor
        !*examine finds the file entry in the directory block
        !*and returns the first block's number in the file
        !*to the caller.

        no = 0
        f == exam(inf)
        %unless f == null %then no = f_first
        %if drive = 1 %and no # 0 %then no = no!k'020000'
        -> reply

write dir: da(dwrite);                    !put directory block back
         rewrite dir;                     ! put list blocks back

reply:  i = map virt(0, -1, myseg);        !release segment
        px_a = no
        pon(px)
        %continue

        !*
        !**
        !***** get next
        !**
        !*

request(get next):

        !*p_b=file descriptor,p_c=last block
        !*get next is given a block of a file and returns
        !*the next block in the file by looking at the link in
        !*the block descriptor.it also reads the block decriptor
        !*entry for the next block to check the protect code.

        drive = inf_unit
        bk = p_c
        %if drive = 1 %then bk = bk&k'17777'
        blk == get block(bk);          !get previous block
        pr = blk_pr;  no = blk_next
        %if no # 0 %start
           blk == get block(no)
           %if blk_pr # pr %then no =- 1 %elsestart
              !! no = -1  is a protect code error
              %if drive = 1 %then no = no!k'020000'
           %finish
        %finish
        -> reply

        !*
        !**
        !***** destroy
        !**
        !*

request(destroy):

        !*destroy removes the file's name from the directory
        !*block and goes down the block descriptor entries for
        !*that file setting all the links and protect codes to
        !*zero(checking the protect codes as it goes.)

        exit = 0;                      !take normal exit
destf:  
        no = 1;               ! file does not exist
        f == exam(inf)
        %unless f == null %start
           no = 0
           bk = f_first;  pr = f_pr

           f = 0;                      ! delete name etc
           f_pr = pr;                  ! restore "pr"

           da(dwrite);                 !write block back immediately
           %cycle
                                       !delete all links and pr
              blk == get block(bk)
              %if blk_pr # pr %start
                 no =- 1;              !corrupt file!!!
                 %exit
              %finish
              %if fblock(drive) <= bk < first free(drive) %then %c
                first free(drive) = bk
              bk = blk_next
              blk = 0;                 ! zero pr and next
              bx_wrm = bx_wrm+1
           %repeat %until bk = 0
        %finish
        -> write dir %if exit = 0
        -> ren tmp;                    !back to rename temp

        !*
        !**
        !***** create file
        !**
        !*

request(create):

        !*a file is created  by finding an empty slot in the directory
        !*block and copying the name into it.a free block is then found
        !*and is deemed to be the first block of the file.a link to
        !*this block is set up and the protect code calculated and
        !*inserted into the block descriptor.

        drive = inf_unit
        nosave = 0
        nosave = appendb(first free(drive))
        %if nosave # 0 %start
           g_fsys = inf_fsys
           g_unit = inf_unit
           f == exam(g);               !find empty slot
           %unless f == null %start
              no = nosave
              f_n = inf_n;             ! copy name
              bx_wrm = bx_wrm+1
              f_pr = ((f_pr+k'010000')&k'170000')!inf_fsys << 6!fno
              f_pr = k'010000' %if f_pr = 0
                                       ! in case of zero pr
              f_first = no
              pr = f_pr
              da(d write);             !put directory block back
              blk == get block(no);    !get block descriptor back
              blk_pr = pr
              bx_wrm = bx_wrm+1
              first free(drive) = no
              %if drive = 1 %then no = no!k'020000'
           %finish
        %finish
        -> reply

        !*
        !**
        !***** append block
        !**
        !*

request(append):

        !*to append a block to a file the current last block
        !*descriptor entry is inspected for the protect code.
        !*the next free block's descriptor is then
        !*updated with this code and a link to this block
        !*is inserted in the last descriptor entry.

        drive = inf_unit
        bk = p_c;                     !get last block
         %if drive = 1 %then bk = bk&k'17777'
        blk == get block(bk);          !get last block
        pr = blk_pr
        no = appendb(bk);              !get new last block
        %if no # 0 %start
           blk_next = 0
           blk_pr = pr
           bx_wrm = bx_wrm+1
           first free(drive) = no
           blk == get block(bk);       !get previuos last block to
                                       ! insert link
           blk_next = no
           %if drive = 1 %then no = no!k'020000'
           bx_wrm = bx_wrm+1
        %finish
        -> reply

        !*
        !**
        !***** rename file
        !**
        !*

request(rename):
request(rename fsys):                  ! files in different fsys

        !*p_bhas existing,p_c has new file descriptor
        !*if the new file does not already exist then the old
        !*file name in the directory block is replaced by
        !*the new.

        no =- 1
        seg2 = p_c >> 13
        %if seg2 = seg %start
           inf2 == record(msa+(p_c&k'17777'))
           %if inf_unit = inf2_unit %start
              %if p_a = rename fsys %start
                 g_fsys = inf2_fsys
                 g_unit = inf2_unit
                 f == exam(g)
                 %unless f == null %start

                    f == exam(inf);     ! get existing file
                    %unless f == null %start; ! doesn't exist
                       bk = f_first;  pr = f_pr
                       f = 0;         ! zero name record
                       bx_wrm = bx_wrm+1
                       da(d write)
                       f == exam(g);        ! get empty slot again
                       f_n = inf2_n;        ! copy name
                       f_first = bk;  f_pr = pr
                       !! bx_wrm = bx_wrm+1 (write dir writes back)
                       no = 0
                    %finish
                 %finish
              %else

                 f == exam(inf2);            !check new file does not exist
                 %if f == null %start
                    f == exam(inf)
                    %if f == null %then no = 1 %elsestart
                       f_n = inf2_n;         ! copy name
                       !! bx_wrm = bx_wrm+1 (write dir writes back)
                       no = 0
                    %finish
                 %finish
              %finish
           %finish
        %finish
        -> write dir

        !*
        !**
        !***** rename temporary file
        !**
        !*

request(rename temp):

        !*this renames a temporary file in the sense that it removes
        !*the temp file marker and destroys the file.

        exit = 1;                      !special exit form directory
        inf_n_name(0) = inf_n_name(0)&x'ff7f'
                                       !remove temp marker
        -> destf

ren tmp:
        inf_n_name(0) = inf_n_name(0)!x'0080'
                                       !put back marker
        f == exam(inf)
        %if f == null %then no =- 1 %elsestart
           f_n_name(0) = f_n_name(0)&x'ff7f'
                                       !not temp now
           !! bx_wrm = bx_wrm+1 (write dir writes back)
           no = 0
        %finish
        -> write dir


request(dir blk no):                ! give block no of directory
        no = dirblk(inf_unit)+inf_fsys
        -> reply

     %repeat
%endofprogram
                                       !not temp now
request(dir blk no):                ! give block no of directory