!******************************
!*  file system handler       *
!*   fsys1s/fsys1y            *
!*  date: 30.Jun.81           *
!******************************
! STACK = 240, STREAMS = 0
!*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  "deimosperm"
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