

     (******************************************************************)
     (*                                                                *)
     (*  Copyright (c) l978 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.          *)
     (*                                                                *)
     (******************************************************************)

 SEGMENT PROCEDURE WRITELINKERINFO(DECSTUFF:BOOLEAN);
	
   TYPE
     LITYPES = (EOFMARK,MODDULE,GLOBREF,PUBBLIC,PRIVVATE,CONNSTANT,GLOBDEF,
                PUBLICDEF,CONSTDEF,EXTPROC,EXTFUNC,SSEPPROC,SSEPFUNC,
                SEPPREF,SEPFREF);
     OPFORMAT = (WORD, BYTE, BIG);
     LIENTRY = RECORD
                 LINAME: ALPHA;
                 CASE LITYPE: LITYPES OF
                   MODDULE,
                   PUBBLIC,
                   PRIVVATE,
                   SEPPREF,
                   SEPFREF:          (FORMAT: OPFORMAT;
                                      NREFS: INTEGER;
                                      NWORDS: INTEGER);
                   CONSTDEF:         (CONSTANT: INTEGER);
                   PUBLICDEF:        (BASEOFFSET: INTEGER);
                   EXTPROC,EXTFUNC,
                   SSEPPROC,SSEPFUNC:(PROCNUM: INTEGER;
                                      NPARAMS: INTEGER;
                                      RANGE: ^INTEGER)
               END;

   VAR FCP,LCP: CTP; CURRENTBLOCK: INTEGER; I: NONRESIDENT;
       EXTNAME: ALPHA; FIC: ADDRRANGE;
       LIREC: LIENTRY;

   PROCEDURE GETREFS(ID,LENGTH: INTEGER);
     VAR LIC: ADDRRANGE; J,MAX,BLOCKCOUNT,COUNT: INTEGER;

     PROCEDURE GETNEXTBLOCK;
     BEGIN
       CURRENTBLOCK := CURRENTBLOCK + 1;
       IF CURRENTBLOCK > REFBLK THEN CURRENTBLOCK := 0;
       IF BLOCKREAD(REFFILE,REFLIST^,1,CURRENTBLOCK) <> 1 THEN;
     END (*GETNEXTBLOCK*) ;

   BEGIN (*GETREFS*)
     IF (NREFS = 1) AND (REFBLK = 0) THEN EXIT(GETREFS);
     COUNT := 0;
     FOR BLOCKCOUNT := 0 TO REFBLK DO
       BEGIN
         IF CURRENTBLOCK < REFBLK THEN MAX := REFSPERBLK ELSE MAX := NREFS-1;
         FOR J := 1 TO MAX DO
           IF ID = REFLIST^[J].KEY THEN
             BEGIN GENWORD(REFLIST^[J].OFFSET); COUNT := COUNT + 1 END;
         IF BLOCKCOUNT < REFBLK THEN GETNEXTBLOCK;
       END;
     LIC := IC; IC := FIC; GENWORD(COUNT); IC := LIC;
     (*NOW FILL REST OF 8-WORD RECORD*)
     FOR J := 1 TO ((8 - (COUNT MOD 8)) MOD 8) DO GENWORD(0)
   END (* GETREFS *) ;

   PROCEDURE GLOBALSEARCH(FCP: CTP);
     VAR NEEDEDBYLINKER: BOOLEAN;

   BEGIN
     NEEDEDBYLINKER := TRUE;
     WITH LIREC,FCP^ DO
       CASE KLASS OF
         TYPES: NEEDEDBYLINKER := FALSE;
         KONST: IF (IDTYPE^.SIZE = 1) AND NOT INMODULE THEN
                  BEGIN LITYPE := CONSTDEF;
                    CONSTANT := VALUES.IVAL
                  END
                ELSE NEEDEDBYLINKER := FALSE;
         FORMALVARS,
         ACTUALVARS:
                BEGIN
                  IF INMODULE THEN
                    BEGIN
                      IF PUBLIC THEN
                        BEGIN LITYPE := PUBBLIC;
                          NWORDS := 0
                        END
                      ELSE
                        BEGIN LITYPE := PRIVVATE;
                          IF KLASS = FORMALVARS THEN
                            NWORDS := PTRSIZE
                          ELSE
                            NWORDS := IDTYPE^.SIZE
                        END;
                      FORMAT := BIG
                    END
                  ELSE
                    BEGIN LITYPE := PUBLICDEF;
                      BASEOFFSET := VADDR
                    END
                END;
         FIELD: NEEDEDBYLINKER := FALSE;
         PROC,
         FUNC:  BEGIN
                  IF PFDECKIND = DECLARED THEN
                    IF PFKIND = ACTUAL THEN
                      IF KLASS = PROC THEN
                        IF EXTURNAL THEN
                          IF SEPPROC THEN LITYPE := SEPPREF
                          ELSE LITYPE := EXTPROC
                        ELSE
                          IF SEPPROC THEN
                            LITYPE := SSEPPROC
                          ELSE NEEDEDBYLINKER := FALSE
                      ELSE (*KLASS = FUNC*)
                        IF EXTURNAL THEN
                          IF SEPPROC THEN LITYPE := SEPFREF
                          ELSE LITYPE := EXTFUNC
                        ELSE
                          IF SEPPROC THEN
                            LITYPE := SSEPFUNC
                          ELSE NEEDEDBYLINKER := FALSE
                    ELSE NEEDEDBYLINKER := FALSE
                  ELSE NEEDEDBYLINKER := FALSE;
                  IF NEEDEDBYLINKER THEN
                    BEGIN
                      LCP := NEXT; NPARAMS := 0;
                      WHILE LCP <> NIL DO
                        BEGIN
                          WITH LCP^ DO
                            IF KLASS = FORMALVARS THEN
                              NPARAMS := NPARAMS + PTRSIZE
                            ELSE
                              IF KLASS = ACTUALVARS THEN
                                IF IDTYPE^.FORM <= POWER THEN
                                  NPARAMS := NPARAMS + IDTYPE^.SIZE
                                ELSE NPARAMS := NPARAMS + PTRSIZE;
                          LCP := LCP^.NEXT
                        END;
                      IF LITYPE IN [SEPPREF,SEPFREF] THEN
                        BEGIN FORMAT := BYTE; NWORDS := NPARAMS END
                      ELSE
                        BEGIN PROCNUM := PFNAME; RANGE := NIL END
                    END
                  END (*PROC,FUNC*);
         MODULE:  BEGIN
                    IF NOT INMODULE THEN NEEDEDBYLINKER := FALSE
                    ELSE
                      BEGIN LITYPE := MODDULE; NWORDS := 0; FORMAT := BYTE END
                  END
       END (*CASE,WITH*);
     IF NEEDEDBYLINKER THEN
       IF SEGTABLE[SEG].SEGKIND = 2 (*SEGPROC*) THEN
         WITH LIREC DO
           IF (LITYPE = CONSTDEF) OR (LITYPE = PUBLICDEF) THEN
             NEEDEDBYLINKER := FALSE;
     IF NEEDEDBYLINKER THEN
       WITH LIREC DO
         BEGIN LINAME := FCP^.NAME;
           FOR LGTH := 1 TO 8 DO GENBYTE(ORD(LINAME[LGTH]));
           GENWORD(ORD(LITYPE));
           CASE LITYPE OF
             MODDULE,
             PUBBLIC,
             PRIVVATE,
             SEPPREF,SEPFREF: BEGIN
                                GENWORD(ORD(FORMAT));
                                FIC := IC; GENWORD(0);
                                GENWORD(NWORDS);
                                IF LITYPE = MODDULE THEN GETREFS(FCP^.SEGID,1)
                                ELSE
                                 IF LITYPE IN [SEPPREF,SEPFREF] THEN
                                   GETREFS(-FCP^.PFNAME,1)
                                 ELSE GETREFS(FCP^.VADDR + 32,FCP^.IDTYPE^.SIZE);
                              END;
             CONSTDEF: BEGIN  GENWORD(CONSTANT); GENWORD(0); GENWORD(0) END;
             PUBLICDEF: BEGIN GENWORD(BASEOFFSET); GENWORD(0); GENWORD(0) END;
             EXTPROC,EXTFUNC:     BEGIN
                                    GENWORD(PROCNUM);
                                    GENWORD(NPARAMS);
                                    GENWORD(ORD(RANGE))
                                  END;
             SSEPPROC,SSEPFUNC:   BEGIN
                                    GENWORD(PROCNUM);
                                    GENWORD(NPARAMS);
                                    GENWORD(ORD(RANGE));
                                    FOR LGTH := 1 TO 8 DO
                                      GENBYTE(ORD(LINAME[LGTH]));
                                    IF LITYPE = SSEPPROC THEN
                                      GENWORD(ORD(SEPPREF))
                                    ELSE GENWORD(ORD(SEPFREF));
                                    GENWORD(ORD(BYTE));
                                    FIC := IC; GENWORD(0); GENWORD(NPARAMS);
                                    GETREFS(-PROCNUM,1)
                                  END
           END(*CASE*)
         END(*WITH*);
     IF IC >= 1024 THEN BEGIN WRITECODE(FALSE); IC := 0 END;

     IF FCP^.LLINK <> NIL THEN GLOBALSEARCH(FCP^.LLINK);
     IF FCP^.RLINK <> NIL THEN GLOBALSEARCH(FCP^.RLINK)

   END (*GLOBALSEARCH*);

 BEGIN (*WRITELINKERINFO*)
   IC := 0;
   IF CODEINSEG THEN ERROR(399);
   IF INMODULE THEN
       CURRENTBLOCK := REFBLK;
   IF DECSTUFF THEN (*SKIP IF NO DECLARATIONPART LINKER INFO*)
     BEGIN FCP := DISPLAY[GLEV].FNAME;
       IF FCP <> NIL THEN GLOBALSEARCH(FCP)
     END;
   (*NOW DO NONRESIDENT PROCS*)
   WITH LIREC DO
     FOR I := SEEK TO DECOPS DO
       IF PFNUMOF[I] <> 0 THEN
         BEGIN
           CASE I OF
             SEEK:       BEGIN LINAME := 'FSEEK   '; NPARAMS := 2 END;
             FREADREAL:  BEGIN LINAME := 'FREADREA'; NPARAMS := 2 END;
             FWRITEREAL: BEGIN LINAME := 'FWRITERE'; NPARAMS := 5 END;
             FREADDEC:   BEGIN LINAME := 'FREADDEC'; NPARAMS := 3 END;
             FWRITEDEC:  BEGIN LINAME := 'FWRITEDE'; NPARAMS := 10 END;
             DECOPS:     BEGIN LINAME := 'DECOPS  '; NPARAMS := 0 END;
           END;
           FOR LGTH := 1 TO 8 DO GENBYTE(ORD(LINAME[LGTH]));
           IF SEPPROC THEN
             BEGIN GENWORD(ORD(SEPPREF));
               GENWORD(ORD(BYTE)); FIC := IC; GENWORD(0); GENWORD(NPARAMS);
               GETREFS(-PFNUMOF[I],1)
             END
           ELSE
             BEGIN GENWORD(ORD(EXTPROC));
               GENWORD(PFNUMOF[I]); GENWORD(NPARAMS); GENWORD(0)
             END;
           PFNUMOF[I] := 0;
         END;
   (* NOW DO EOFMARK END-RECORD*)
   FOR LGTH := 1 TO 8 DO GENBYTE(ORD(' '));
   GENWORD(ORD(EOFMARK)); GENWORD(LCMAX);
   GENWORD(0);GENWORD(0);
   WRITECODE(TRUE);
   CLINKERINFO := FALSE;
   IF DECSTUFF THEN DLINKERINFO := FALSE
 END (*WRITELINKERINFO*);

 SEGMENT PROCEDURE UNITPART(FSYS: SETOFSYS);
   VAR UMARKP: TESTP;

   PROCEDURE OPENREFFILE;
   BEGIN
     REWRITE(REFFILE,'*SYSTEM.INFO[*]');
     IF IORESULT <> 0 THEN ERROR(402)
   END (* OPENREFFILE *) ;

   PROCEDURE UNITDECLARATION(FSYS: SETOFSYS; VAR UMARKP:TESTP);
     VAR LCP: CTP; FOUND: BOOLEAN; LLEXSTK: LEXSTKREC;
   BEGIN
     IF INMODULE THEN ERROR(182 (* NESTED MODULES NOT ALLOWED *));
     IF CODEINSEG THEN
       BEGIN ERROR(399); SEGINX := 0; CURBYTE := 0 END;
     WITH LLEXSTK DO
       BEGIN
         DOLDTOP := TOP;
         DOLDLEV := LEVEL;
         POLDPROC := CURPROC;
         SOLDPROC := NEXTPROC;
         DOLDSEG := SEG;
         DLLC := LC;
         PREVLEXSTACKP := TOS
       END;
     SEG := NEXTSEG;
     NEXTSEG := NEXTSEG + 1;
     IF NEXTSEG > MAXSEG THEN ERROR(250);
     NEXTPROC := 1;
     PUBLICPROCS := FALSE;
     INMODULE := TRUE;
     INSYMBOL;
     IF SY <> IDENT THEN ERROR(2)
     ELSE
       BEGIN FOUND := FALSE;
         LCP := MODPTR;
         WHILE (LCP <> NIL) AND NOT FOUND DO
           IF LCP^.NAME <> ID THEN LCP := LCP^.NEXT
           ELSE BEGIN FOUND := TRUE; ERROR(101) END;
         IF NOT FOUND THEN
           BEGIN NEW(LCP,MODULE);
             WITH LCP^ DO
               BEGIN NAME := ID; IDTYPE := NIL; NEXT := MODPTR;
                 KLASS := MODULE; SEGID := SEG
               END;
             MODPTR := LCP
           END;
       END;
     SEGTABLE[SEG].SEGNAME := ID;
     MARK(UMARKP);
     NEW(REFLIST);
     NEW(TOS);
     TOS^ := LLEXSTK;
     LEVEL := 1;
     IF TOP < DISPLIMIT THEN
       BEGIN TOP := TOP +1;
         WITH DISPLAY[TOP] DO
           BEGIN FNAME := NIL; FFILE := NIL; FLABEL := NIL; OCCUR := BLCK END;
         IF LCP <> NIL THEN ENTERID(LCP)
       END
     ELSE ERROR(250);
     INSYMBOL;
     IF SY = SEMICOLON THEN INSYMBOL ELSE ERROR(14)
   END (*UNITDECLARATION*) ;

 BEGIN (*UNITPART*)
   OPENREFFILE;
   REPEAT
     RESET(REFFILE); NREFS := 1; REFBLK := 0;
     IF (SY = SEPARATSY) THEN
       BEGIN SEPPROC := TRUE;
         INSYMBOL; IF SY <> UNITSY THEN ERROR(24)
       END
     ELSE
       SEPPROC := FALSE;
     UNITDECLARATION(FSYS,UMARKP);
     IF SEPPROC THEN SEGTABLE[SEG].SEGKIND := 4 ELSE SEGTABLE[SEG].SEGKIND := 3;
     SEGTABLE[SEG].TEXTADDR := CURBLK;
     WRITETEXT;
     IF SY = INTERSY THEN INSYMBOL
     ELSE ERROR(22);
     ININTERFACE := TRUE;
     DECLARATIONPART(FSYS);
     IF PUBLICPROCS THEN
       BEGIN
         ININTERFACE := FALSE;
         IF SY <> IMPLESY THEN BEGIN ERROR(23); SKIP(FSYS - STATBEGSYS) END
         ELSE INSYMBOL;
         BLOCK(FSYS - [SEPARATSY,UNITSY,INTERSY,IMPLESY]);
         IF REFBLK > 0 THEN
           IF BLOCKWRITE(REFFILE,REFLIST^,1,REFBLK) <> 1 THEN ERROR(402);
         WRITELINKERINFO(TRUE);
       END
     ELSE
       BEGIN DLINKERINFO := FALSE;
         WITH SEGTABLE[SEG] DO
           BEGIN CODELENG := 0; DISKADDR :=CURBLK; SEGKIND := 0 END;
       END;
     SEPPROC := FALSE; (*FALSE WHENEVER NOT INMODULE*)
     INMODULE := FALSE;
     IF SY = ENDSY THEN INSYMBOL
     ELSE BEGIN ERROR(13); SKIP(FSYS) END;
     IF SY <> PERIOD THEN
       IF SY = SEMICOLON THEN INSYMBOL
       ELSE ERROR(14);
     WITH TOS^ DO
         BEGIN
           TOP := DOLDTOP;
           LEVEL := DOLDLEV;
           CURPROC := POLDPROC;
           NEXTPROC := SOLDPROC;
           SEG := DOLDSEG;
           LC := DLLC;
         END;
     TOS := TOS^.PREVLEXSTACKP;
     RELEASE(UMARKP)
   UNTIL NOT (SY IN [UNITSY,SEPARATSY]);
   CLOSE(REFFILE)
 END (*UNITPART*);
