! Filestore copier -- writer program
! Listens for commands from the reader (create user, create file, write data).
! If the incoming partition no. is invalid for the destination filestore
! (e.g. copying C -> A) the user is placed on the highest partition available.

%include "Sys_A:Config.Inc"
%include "System:DisqIO.Inc"

%include "Formats.Inc"
%include "Utility.Inc"

%include "Inc:Util.Imp"
%include "Inc:FS.Imp"

%constinteger port = 15
%constinteger mask = 1 << port

! Bad block list format (from System:FSys)
%constinteger bad limit = 512 - 3
%recordformat bad fm((%integer stamp, %integerarray bad(1 : bad limit),
                      %integer last bad, checksum) %c
                 %or %integerarray x(1 : 512))

%ownrecord(bad fm) bad list = 0

%routine read bad list
   %integername x
   %integer i, c
      printstring("Get bad block list from ")
      write(sy2 start + bad area(1), 0)
      newline
      x == transfer(d read, 2048, (sy2 start + bad area(1)) << 9,
                    integer(addr(bad list)))
      %cycle;  %repeat %until x # 0
      %if x # 2048 %start
         printstring("Failed to read bad block list")
         newline
         %stop
      %finish
      c = 0
      c <- c + bad list_x(i) %for i = 1, 1, 512
      %unless c = 0 %start
         printstring("Bad block list checksum error")
         newline
         %stop
      %finish
      %if bad list_last bad = 0 %start
         printstring("No bad blocks")
         newline
      %else
         printstring("Bad blocks:");  newline
         %for i = 1, 1, bad list_last bad %cycle
            write(bad list_bad(i), 8)
            newline %if i & 7 = 0
         %repeat
         newline %if bad list_last bad & 7 # 0
      %finish
%end

%predicate bad block(%integer block)
   %integer i
      %false %if bad list_last bad = 0
      %for i = 1, 1, bad list_last bad %cycle
         %true %if block = bad list_bad(i)
         %false %if block > bad list_bad(i)
      %repeat
      %false
%end

%begin
   %record(message fm) m
   %record(directory fm) directory
   %record(header fm)%name h
   %record(entry fm)%name f
   %record(extent fm)%name e
   %integer partition, slot
   %integerarray slots used(0 : last partition)
   %integerarray blocks used(0 : last partition)
   %integer i, n, next sequence = 0
   %integer current user = 0, current extent, current file, block, next block
   %integername x
      set terminal mode(no page)
      slots used(i) = -1 %for i = 0, 1, last partition
      blocks used(i) = 0 %for i = 0, 1, last partition
      h == directory_header
      read bad list
      prompt("Reader: ");  rhex(i)
      ether open(port, i << 8 ! port)
      %cycle
         next sequence = next sequence + 1
         n = ether read(port, byteinteger(addr(m)), message size)
         %if m_sequence # next sequence %start
            printstring("Bad sequence ");  write(m_sequence, 0)
            newline
            next sequence = next sequence - 1;  ! re-increment next time
         %else %if m_code = create user
            %exit %if m_new user_owner = 0;  ! Null create to finish
            directory = 0
            partition = m_partition
            partition = last partition %unless 0 <= partition <= last partition
            slot = slots used(partition)
            %cycle
               slot = slot + 1
               %if slot > u per p %start
                  printstring("Partition register full")
                  newline
                  current user = 0
                  %exit
               %finish
               %if bad block(pd start(partition) + 4 * slot) %c
                     %or bad block(pd start(partition) + 4 * slot + 1) %c
                     %or bad block(pd start(partition) + 4 * slot + 2) %c
                     %or bad block(pd start(partition) + 4 * slot + 3) %start
                  printstring("Potential bad directory: ")
                  write(partition, 0);  write(slot, 1)
                  newline
               %else
                  h = m_new user
                  h_perms = h_perms & permissions mask;  ! Lose file count
                  x == transfer(d write ! d verify, 2048,
                                (pd start(partition) + 4 * slot) << 9,
                                integer(addr(directory)))
                  %cycle;  %repeat %until x # 0
                  %if x # 2048 %start
                     printstring("Error writing directory ")
                     printstring(unpack(h_owner, 0));  newline
                  %else
                     current user = h_owner
                     current file = 1
                     current extent = extent limit + 1
                     slots used(partition) = slot
                     printstring(unpack(current user, 0))
                     printstring(" (");  write(partition, 0)
                     print symbol('.');  write(slot, 0)
                     printstring(") created")
                     newline
                     %exit
                  %finish
               %finish
            %repeat
         %else %if m_code = create file
            %if current user = 0 %start
               printstring("Ignoring file ")
               printstring(unpack(m_new file_name 1, m_new file_name 2))
               newline
               current file = 0
            %else
               f == directory_file(current file)
               write(partition, 1);  write(slot, 1)
               printstring(" Create ")
               printstring(unpack(current user, 0))
               print symbol(':')
               printstring(unpack(m_new file_name 1, m_new file_name 2))
               newline
               f = m_new file
               f_status = f_status & (\ dud file)
               f_extents = -1;  ! Meantime
               next block = -1
            %finish
         %else %if m_code = write block
            %if current file = 0 %start
               printstring("Ignoring block");  newline
            %else %if m_bytes < 0
               ! End of file (N.B. can have ZERO as valid byte count!)
               h_files = h_files + 1
               current file = current file + 1
               x == transfer(d write ! d verify, 2048,
                             (pd start(partition) + 4 * slot) << 9,
                             integer(addr(directory)))
               %cycle;  %repeat %until x # 0
               %if x # 2048 %start
                  printstring("Error writing directory ")
                  printstring(unpack(h_owner, 0));  newline
               %finish
               next block = -1
            %else
               %cycle
                  block = blocks used(partition)
                  blocks used(partition) = block + 1
                  %exit %unless bad block(block + p start(partition))
                  printstring("Ignoring bad block ")
                  write(block + p start(partition), 0)
                  newline
               %repeat
               %if block # next block %start
                  current extent = current extent - 1
                  e == directory_extent(current extent)
                  e_start <- block;  e_size = 0
                  f_extents = current extent
               %finish
               e_size = e_size + 1
               x == transfer(d write ! d verify, 512,
                             (block + p start(partition)) << 9,
                             integer(addr(m_data(0))))
               %cycle;  %repeat %until x # 0
               %if x # 512 %start
                  printstring("Dud block in file ")
                  printstring(unpack(current user, 0))
                  print symbol(':')
                  printstring(unpack(f_name 1, f_name 2))
                  newline
               %finish
               next block = block + 1
               f_bytes = (f_bytes & permissions mask) ! m_bytes
            %finish
         %else
            printstring("Unknown request code ")
            write(m_code, 0)
            printstring(", mesage ");  write(m_sequence, 0)
            newline
            m_code = -1
         %finish
         %cycle
            NAK = 0
            ether write(port, byteinteger(addr(m)), 8)
            %cycle;  %repeat %until ACK & mask # 0
            %exit %if NAK & mask = 0
            printstring("NAK");  newline
         %repeat
      %repeat
      printstring("All done.");  newline
%end %of %program
