
     (******************************************************************)
     (*                                                                *)
     (*  Copyright (c) 1978 Regents of the University of California.   *)
     (*  Permission to copy or distribute this software or documen-    *)
     (*  tation in hard or soft copy granted only by written license   *)
     (*  obtained from the Institute for Information Systems.          *)
     (*                                                                *)
     (******************************************************************)

     {
     *  Readsrcseg determines the final segment size after adding
     *  in the external procs/funcs, allocates enough area for the
     *  entire output code seg, reads in the original code (or uses
     *  identity segment for sephost special case), and splits the
     *  segdict off from the code.  For all procs to-be-linked, a new
     *  destbase position is assigned in seg and the new proc num is
     *  set up in pdict.  The segment number field of the pdict is
     *  also updated to the value of s.  All is ready to copy in the
     *  sep procs/funcs.  The values for segbase and segleng are set
     *  here too.
     }

     procedure readsrcseg;
       var orgleng, addr,
           addleng, addprocs,
           nextspot: integer;
           last: 0..MAXPROC;
           wp: workp;
           lheap: ^integer;

         {
         *  Readnsplit arranges for the source seg to be placed in
         *  room allocated for segbase.  This may involve disk read
         *  or perhaps only creating an empty segment.  In any case
         *  segbase points at lowest addr, and nextspot is pointed
         *  at the next place code can be copied into.  This is used
         *  for destbase assignment in readsrcseg.
         }

         procedure readnsplit;
           var nblocks, n, pdleng,
               pddelta, nprocs: integer;
               cp0, cp1: codep;
         begin
           nblocks := (segleng+511) div 512;
           if memavail-400 < nblocks*256 then
             begin
               error('no mem room');
               exit(linker)
             end;
           n := nblocks;
           repeat
             { alloc heap space }
             new(cp1);
             n := n-1
           until n <= 0;
           if sephost then
             begin { set up identity seg }
               storeword(0, segbase, segleng-2);
               nextspot := 0
             end
           else
             begin { read from disk }
               nblocks := (orgleng+511) div 512;
               if blockread(seginfo[s]^.srcfile^.code^, segbase^,
                         nblocks, addr) <> nblocks then
                 begin
                   error('seg read err');
                   exit(linker)
                 end;
               pddelta := segleng-orgleng;
               nprocs := fetchbyte(segbase, orgleng-1);
               pdleng := nprocs*2+2;
               nextspot := orgleng-pdleng;
               cp0 := getcodep(ord(segbase)+orgleng-pdleng);
               cp1 := getcodep(ord(segbase)+segleng-pdleng);
               if cp0 <> cp1 then
                 begin { move proc dict }
                   n := pdleng;
                   while n > 2 do
                     begin
                       storeword(pddelta+fetchword(segbase, orgleng-n),
                           segbase, orgleng-n);
                       n := n-2
                     end;
                   moveright(cp0^, cp1^, pdleng);
                   fillchar(cp0^, pddelta, 0)
                 end
             end
         end { readnsplit } ;

     begin { readsrcseg }
       if sephost then
         orgleng := 2
       else
         with seginfo[s]^, srcfile^.segtbl.diskinfo[srcseg] do
           begin
             orgleng := codeleng;
             addr := codeaddr
           end;
       addleng := 0;
       addprocs := 0;
       wp := procs;
       while wp <> NIL do
         begin { add up final seg size }
           addleng := addleng+wp^.defsym^.entry.place^.length;
           if wp^.newproc = 0 then
             addprocs := addprocs+1;
           wp := wp^.next
         end;
       mark(lheap);
       segbase := getcodep(ord(lheap));
       segleng := orgleng+addleng+2*addprocs;
       if segleng <= 0 then
         begin
           error('size oflow');
           exit(linker)
         end;
       readnsplit;
       last := fetchbyte(segbase, segleng-1);
       wp := procs;
       while wp <> NIL do
         begin { assign places in code seg }
           with wp^.defsym^.entry.place^ do
             begin
               destbase := nextspot;
               nextspot := nextspot+length
             end;
           if wp^.newproc = 0 then
             begin { assign new proc # }
               last := last+1;
               if last > MAXPROC then
                 begin
                   error('proc num oflow');
                   last := 1
                 end;
               wp^.newproc := last
             end;
           wp := wp^.next
         end;
       storebyte(last, segbase, segleng-1);
       storebyte(s, segbase, segleng-2)
     end { readsrcseg } ;

     {
     *  Copyinprocs goes through procs list and copies procedure
     *  bodies from the sep segs into the dest code segment into
     *  locations set up in readsrcseg. If all goes right, we should
     *  fill dest seg to the exact byte.  The proc dict is
     *  updated to show procedures' position.
     }

     procedure copyinprocs;
       var cp0, cp1, pdp,
           jtab, sepbase: codep;
           wp: workp;
           cursp: segp;
           lheap: ^integer;

         {
         *  Readsepseg reads the sep seg in sp onto the heap as
         *  done in Phase 2.  We set up sepbase and cursp for
         *  copyinprocs.
         }

         procedure readsepseg(sp: segp);
           var n, nblocks: integer;
         begin
           release(lheap);
           n := sp^.srcfile^.segtbl.diskinfo[sp^.srcseg].codeleng;
           nblocks := (n+511) div 512;
           if memavail-400 < nblocks*256 then
             begin
               error('out of mem');
               exit(linker)
             end;
           n := nblocks;
           repeat
             new(sepbase);
             n := n-1
           until n <= 0;
           sepbase := getcodep(ord(lheap));
           if blockread(sp^.srcfile^.code^, sepbase^, nblocks,
               sp^.srcfile^.segtbl.diskinfo[sp^.srcseg].codeaddr) <> nblocks then
             begin
               error('sep seg read err');
               exit(linker)
             end;
           cursp := sp
         end { readsepseg } ;

     begin { copyinprocs }
       sepbase := NIL;
       cursp := NIL;
       mark(lheap);
       wp := procs;
       while wp <> NIL do
         with wp^, defsym^.entry do
           begin { copy in each proc }
             if cursp <> defseg then
               readsepseg(defseg);
             if talkative then
               begin
                 write('   Copying ');
                 if litype = SEPPROC then
                   write('proc ')
                 else
                   write('func ');
                 writeln(name)
               end;
             cp0 := getcodep(ord(sepbase)+place^.srcbase);
             cp1 := getcodep(ord(segbase)+place^.destbase);
             moveleft(cp0^, cp1^, place^.length);
             jtab := getcodep(ord(segbase)+place^.destbase+place^.length-2);
             if fetchbyte(jtab, 0) <> 0 then
               storebyte(newproc, jtab, 0);
             pdp := getcodep(ord(segbase)+segleng-2*newproc-2);
             storeword(ord(pdp)-ord(jtab), pdp, 0);
             wp := next
           end;
       release(lheap)
     end { copyinprocs } ;

     {
     *  Fixuprefs is called to search through reflists and fix
     *  operand fields of P-code and native code to refer to the
     *  resolved values.  If fixallrefs is true, then all pointers
     *  in the ref lists are used, otherwise the reference pointers
     *  are checked to see if they occur in the procs to-be-linked.
     }

     procedure fixuprefs(work: workp; fixallrefs: boolean);
       var n, i, ref, val: integer;
           wp, wp1: workp;
           rp: refp;
           skipit: boolean;
           r: packed record
                case boolean of
                  TRUE:  (integ: integer);
                  FALSE: (lowbyte: 0..255;
                          highbyte: 0..255)
              end { r } ;
     begin
       while work <> NIL do
         with work^, refsym^.entry do
           begin { for each work item }
                 { figure resolve val }
             case litype of
               SEPPREF,
               SEPFREF:  val := defproc^.newproc;
               UNITREF:  val := defsegnum;
               CONSTREF: val := defsym^.entry.constval;
               GLOBREF:  val := defsym^.entry.icoffset+
                                defproc^.defsym^.entry.place^.destbase;
               PUBLREF,
               PRIVREF:  begin
                           if litype = PRIVREF then
                             val := newoffset
                           else
                             val := defsym^.entry.baseoffset;
                           if format = WORD then
                             val := (val-1)*2+MSDELTA
                           else { assume BIG }
                             if val >= 0 then
                               begin
                                 r.highbyte := val mod 256;
                                 r.lowbyte := val div 256 + 128;
                                 val := r.integ
                               end
                             else
                               error('addr oflow')
                         end
             end;
             n := nrefs;
             rp := reflist;
             while rp <> NIL do
               begin
                 if n > 8 then
                   begin
                     i := 7;
                     n := n-8
                   end
                 else
                   i := n-1;
                 repeat
                   ref := rp^.refs[i];
                   skipit := not fixallrefs;
                   if skipit then
                     begin { see if pertinent }
                       wp := NIL;
                       wp1 := procs;
                       while wp1 <> NIL do
                         if wp1^.defseg = refseg then
                           begin { find matching seg }
                             wp := wp1;
                             wp1 := NIL
                           end
                         else
                           wp1 := wp1^.next;
                       while (wp <> NIL) and skipit do
                         if wp^.defseg = refseg then
                           with wp^.defsym^.entry.place^ do
                             if ref >= srcbase then
                               if ref < srcbase+length then
                                 begin
                                   ref := ref-srcbase+destbase;
                                   skipit := FALSE
                                 end
                               else
                                 wp := wp^.next
                             else
                               wp := NIL
                         else
                           wp := NIL
                     end;
                   if not skipit then
                     case format of { fix up this ref }
                       WORD:  storeword(val+fetchword(segbase, ref),
                                                 segbase, ref);
                       BYTE:  storebyte(val, segbase, ref);
                       BIG:   storeword(val, segbase, ref)
                     end;
                   i := i-1
                 until i < 0;
                 rp := rp^.next
               end;
             work := next
           end
     end { fixuprefs } ;

     {
     *  writetocode takes the finalized destseg and puts it in
     *  the output code file.  This also involves setting up values
     *  in the final segtable for writeout just before locking it.
     }

     procedure writetocode;
       var nblocks: integer;
           jtab: codep;
     begin
       if hostsp = seginfo[s] then
         begin { fix up baselc }
           jtab := getcodep(ord(segbase)+segleng-4);
           jtab := getcodep(ord(jtab)-fetchword(jtab, 0));
           storeword(nextbaselc*2-6, jtab, -8)
         end;
       with seginfo[s]^, segtbl do
         begin
           nblocks := (segleng+511) div 512;
           if blockwrite(code, segbase^, nblocks, nextblk) <> nblocks then
             begin
               error('code write err');
               exit(linker)
             end;
           diskinfo[s].codeaddr := nextblk;
           diskinfo[s].codeleng := segleng;
           segname[s] := srcfile^.segtbl.segname[srcseg];
           segkind[s] := LINKED;
           nextblk := nextblk+nblocks
         end
     end { writetocode } ;

     {
     *  Linksegment is called for each segment to be placed into
     *  the final code file.  The global var s has the seginfo index
     *  pertaining to the segment, and all the other procedures of
     *  Phase 3 are called from here.  This proc facilitates linking
     *  the master seg separatly from the other segs to ensure that
     *  the DATASZ of the outer block correctly reflects the number
     *  of PRIVREF words allocated by resolve.
     }

     procedure linksegment;

         {
         *  Writemap is called for each seg to write some
         *  info into map file.
         }

         procedure writemap;
           var wp: workp;
               b: boolean;
         begin
           with seginfo[s]^ do
             writeln(map, 'Seg # ',s,', ', srcfile^.segtbl.segname[srcseg]);
           wp := procs;
           if wp <> NIL then
             writeln(map, '   Sep procs');
           while wp <> NIL do
             with wp^.defsym^.entry do
               begin
                 write(map, '      ', name);
                 if litype = SEPPROC then
                   write(map, ' proc')
                 else
                   write(map, ' func');
                 write(map, ' # ', wp^.newproc: 3);
                 write(map, '    base =', place^.destbase: 6);
                 write(map, '    leng =', place^.length: 5);
                 writeln(map);
                 wp := wp^.next
               end;
           for b := FALSE to TRUE do
             begin
               if b then
                 begin
                   wp := other;
                   if wp <> NIL then
                     writeln(map, '   Sep proc refs')
                 end
               else
                 begin
                   wp := local;
                   if wp <> NIL then
                     writeln(map, '   Local seg refs')
                 end;
               while wp <> NIL do
                 with wp^.defsym^.entry do
                   begin
                     write(map, '      ', name);
                     case litype of
                       SEPPROC,
                       SEPFUNC:   ;
                       PUBLDEF:   write(map, ' public LC =', baseoffset: 5);
                       CONSTDEF:  write(map, ' const val =', constval: 6);
                       PRIVREF:   write(map, ' privat LC =', wp^.newoffset: 5);
                       UNITREF:   write(map, ' unit seg# =', wp^.defsegnum: 3);
                       GLOBDEF:   write(map, ' glob def in ',
                                     wp^.defproc^.defsym^.entry.name,
                                     ' @', icoffset: 5)
                     end;
                     writeln(map);
                     wp := wp^.next
                   end
             end;
           writeln(map)
         end { writemap } ;

     begin { linksegment }
       sephost := FALSE;
       segbase := NIL;
       segleng := 0;
       if talkative then
         with seginfo[s]^ do
           writeln('Linking ',
                    srcfile^.segtbl.segname[srcseg], ' # ', s);
       buildworklists;
       if errcount = 0 then
         begin
           readsrcseg;
           if mapname <> '' then
             writemap;
           copyinprocs;
           fixuprefs(local, TRUE);
           fixuprefs(other, FALSE);
           writetocode
         end;
       if sephost then
         seplist := seginfo[s]^.next;
       release(heapbase)
     end { linksegment } ;

 begin { phase3 }
   if not useworkfile then
     begin
       write('Output file? ');
       readln(fname);
       useworkfile := fname = ''
     end;
   if useworkfile then
     rewrite(code, '*SYSTEM.WRK.CODE[*]')
   else
     rewrite(code, fname);
   if IORESULT <> 0 then
     begin
       error('Code open err');
       exit(linker)
     end;
   nextblk := 1;
   { clear output seg table }
   fillchar(segtbl, sizeof(segtbl), 0);
   with segtbl do
     for s := 0 to MAXSEG do
       begin
         segname[s] := '        ';
         segkind[s] := LINKED
       end;
   if mapname <> '' then
     begin
       rewrite(map, mapname);
       if IORESULT <> 0 then
         begin
           writeln('Can''t open ', mapname);
           mapname := ''
         end
       else
         begin
           write(map, 'Link map for ');
           if hostsp <> NIL then
             writeln(map, hostsp^.srcfile^.segtbl.segname[hostsp^.srcseg])
           else
             writeln(map, 'assem host');
           writeln(map)
         end
     end;
   mark(heapbase);
   unitwrite(3, heapbase^, 35);
   { link all but host }
   for s := 0 to MAXSEG do
     if (seginfo[s] <> NIL)
     and (seginfo[s] <> hostsp) then
       linksegment;
   { link host last! }
   if hostsp <> NIL then
     begin
       s := MASTERSEG;
       linksegment
     end;
   if blockwrite(code, segtbl, 1, 0) <> 1 then
     error('Code write err');
   if errcount = 0 then
     begin { final cleanup }
       close(code, LOCK);
       if useworkfile then
         with userinfo do
           begin
             gotcode := TRUE;
             codevid := syvid;
             codetid := 'SYSTEM.WRK.CODE'
           end;
       if mapname <> '' then
         begin
           if hostsp <> NIL then
             writeln(map, 'next base LC = ', nextbaselc);
           close(map, LOCK)
         end
     end

 end { phase3 } ;


