Initial revision
authorkeie <none@none>
Thu, 12 Jul 1984 12:48:33 +0000 (12:48 +0000)
committerkeie <none@none>
Thu, 12 Jul 1984 12:48:33 +0000 (12:48 +0000)
util/ass/ass00.c [new file with mode: 0644]
util/ass/ass00.h [new file with mode: 0644]

diff --git a/util/ass/ass00.c b/util/ass/ass00.c
new file mode 100644 (file)
index 0000000..3234b1e
--- /dev/null
@@ -0,0 +1,537 @@
+#include        "ass00.h"
+#include        "assex.h"
+
+/*
+ * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands.
+ *
+ *          This product is part of the Amsterdam Compiler Kit.
+ *
+ * Permission to use, sell, duplicate or disclose this software must be
+ * obtained in writing. Requests for such permissions may be sent to
+ *
+ *      Dr. Andrew S. Tanenbaum
+ *      Wiskundig Seminarium
+ *      Vrije Universiteit
+ *      Postbox 7161
+ *      1007 MC Amsterdam
+ *      The Netherlands
+ *
+ */
+
+/*
+** Main routine of EM1-assembler/loader
+*/
+
+main(argc, argv)
+       int     argc;
+       char    **argv;
+{
+       /*
+        * Usage: ass [-[d][p][m][u]] [-s(s/m/l)] [ [file] [flag] ] ...
+        *   The d flag can be repeated several times, resulting in more
+        *        debugging information.
+        */
+#ifdef EM_WSIZE
+       char workspace[2000] ;
+#else
+       char workspace[6000] ;
+#endif
+       register char *cp ;
+       register int argno ;
+
+       progname = argv[0];
+       for ( cp=argv[0] ; *cp ; ) if ( *cp++ == '/' ) progname= cp;
+       for ( argno=1 ; argno<argc ; argno++ ) {
+               if ( argv[argno][0] == '-' && LC(argv[argno][1]) == 's') {
+                       getsizes(&argv[argno][2]);
+                       break ;
+               }
+       }
+       /* A piece of the interpreter's stack frame is used as
+          free area initially */
+       freearea( (area_t) workspace, (unsigned) sizeof workspace ) ;
+       getcore();
+       init_files();
+       init_vars();
+       while ( --argc )
+               argument(*++argv);
+       finish_up();
+       exit(nerrors!=0);
+}
+
+getcore() {
+       register size_t *p;
+       size_t bytes;
+       register unsigned n ;
+       register char *base ;
+
+       /*
+        * xglobs[] should be located in front of mglobs[], see upd_reloc()
+        */
+
+       p = oursize; n = 0;
+       n += (bytes.n_glab = p->n_glab * (sizeof *xglobs));
+       n += (bytes.n_mlab = p->n_mlab * (sizeof *mglobs));
+       n += (bytes.n_mproc = p->n_mproc * (sizeof *mprocs));
+       n += (bytes.n_xproc = p->n_xproc * (sizeof *xprocs));
+       n += (bytes.n_proc = p->n_proc * (sizeof *proctab));
+       base = getarea(n);
+       zero(base,n);
+       xglobs = gbp_cast base; base += bytes.n_glab;
+       mglobs = gbp_cast base; base += bytes.n_mlab;
+       mprocs = prp_cast base; base += bytes.n_mproc;
+       xprocs = prp_cast base; base += bytes.n_xproc;
+       proctab = ptp_cast base; base += bytes.n_proc;
+}
+
+getsizes(str) char *str; {
+
+       /*
+        * accepts -ss (small), -sm (medium), -sl (large)
+        */
+
+       switch(LC(*str)) {
+               default:error("bad size option %s",str);
+       case 's':       oursize = &sizes[0]; break;
+       case 'm':       oursize = &sizes[1]; break;
+       case 'l':       oursize = &sizes[2]; break;
+       }
+}
+
+char oflag;
+
+argument(arg) char *arg; {
+       register w;
+
+       /*
+        * This routine decides what to do with each argument.
+        * It recognises flags and modules.
+        * Furthermore, it knows a library when it sees it and
+        * call archive() to split it apart.
+        */
+
+       if (oflag) {
+               eout = arg;
+               oflag=0;
+               return;
+       }
+       if(*arg == '-') {
+               flags(arg);
+               return;
+       }
+       curfile = arg;  /* for error messages etc. */
+       if ((ifile = fopen(arg,"r")) == NULL) {
+               error("can't open %s",arg);
+               return;
+       }
+       inpoff = 2;
+       if ((w = (unsigned)get16()) == sp_magic )
+               read_compact();
+       else if (w == ARMAG) {
+               archmode = TRUE;
+               archive();
+               archmode = FALSE;
+       } else
+               error("%s: bad format",arg);
+       if (fclose(ifile) == EOF)
+               ;
+}
+
+/*
+** process flag arguments
+*/
+
+static int memflg ;
+
+flags(arg)
+       char    *arg;
+{
+       register char   *argp;
+       register on;
+
+       argp = arg;
+       while (*++argp)
+       {
+               switch(LC(*argp))
+               {
+                       case 'd':       d_flag++;break;
+                       case 'r':       r_flag++;break;
+                       case 's':       return ; /* s-flag is already scanned */
+#ifdef MEMUSE
+                       case 'm':       memflg++ ; break ;
+#endif
+                       case 'p':       ++procflag;break;
+#ifdef DUMP
+                       case 'u':       ++c_flag;break;
+#endif
+                       case 'o':       ++oflag; break;
+                       case 'w':       ++wflag; break;
+#ifdef JOHAN
+                       case 'j':       ++jflag; break;
+#endif
+                       case '-':
+                       case '+':
+                               on = (*argp == '+');
+                               while (*++argp) switch(LC(*argp)) {
+                               case 't': if (on) intflags |= 01;
+                                         else intflags &= ~01;
+                                         break;
+                               case 'p': if (on) intflags |= 02;
+                                         else intflags &= ~02;
+                                         break;
+                               case 'f': if (on) intflags |= 04;
+                                         else intflags &= ~04;
+                                         break;
+                               case 'c': if (on) intflags |= 010;
+                                         else intflags &= ~010;
+                               case 'e': if (on) intflags |= 040;
+                                         else intflags &= ~040;
+                                         break;
+                               default:
+                                 error("bad interpreter option %s",argp);
+                               }
+                               --argp;
+                               break;
+                       default:
+                               error("bad flag %s",argp);
+                                       break;
+               }
+       }
+}
+
+do_proc() {
+       /* One procedure has been read and will be processed.
+        *
+        * NOTE: The numbers of the passes, 1 3 4 and 5, are a remainder
+        *       of ancient times.
+        */
+
+       dump(1); if ( memflg>2 )memuse();
+       pass_3();  dump(3);
+       pass_4();  dump(4);
+       pass_5();  if ( memflg>2 ) memuse() ;
+       endproc();  if ( memflg>1 ) memuse() ;
+}
+
+archive() {
+       register i;
+       register char *p;
+
+       /*
+        * Read a library.
+        * The format of the libary used is that of a UNIX/V7(PDP)-archive.
+        *
+        * NOTE: If it was allowed for an archive to contain
+        *       obligatory modules as well as optionals,
+        *       it would not be possible to speed up things a bit
+        *       by stopping when all references are resolved.
+        *       This is the only reason.
+        */
+
+       for(;;) {
+               if (unresolved == 0) {  /* no use for this library anymore */
+                       return;
+               }
+               p = chp_cast &archhdr;
+               if ((i = fgetc(ifile))==EOF ) {
+                       return;
+               }
+               *p++ = i;
+               for (i=1;i< sizeof archhdr.ar_name; i++)
+                       *p++ = get8();
+               for (i=0;i<8;i++) get8();
+               archhdr.ar_size= ((long)get16()<<16) ;
+               archhdr.ar_size+= (unsigned)get16();
+               inpoff = 0;     libeof = archhdr.ar_size;
+               /*
+                * UNIX archiveheader is read now, now process the contents
+                * of it. Note that recursive archives are not implemented.
+                *
+                * The variable libeof is used by get8() to check
+                * whether or not we try to pass the library-boundary.
+                */
+               if ( get16() == sp_magic ) {
+                       read_compact();
+               } else
+                       error("bad archive entry");
+               skipentry();
+               libeof = 0;
+       }       /* up to the next entry */
+}
+
+skipentry() {
+
+       /*
+        * for some reason the rest of this library entry needs to be
+        * skipped. Do that now.
+        */
+       while(inpoff<libeof)
+               get8();
+       if(odd(libeof))                 /* archive entries are evensized */
+               if (fgetc(ifile) == EOF)   /* except maybe the last one */
+                       ;
+}
+
+init_vars() {
+
+       /*
+        * A small collection of variables is initialized.
+        * This occurs only for those that couldn't be initialized
+        * at compile-time.
+        */
+
+}
+
+init_files() {
+
+       /*
+        * The temporary files on which text and data are kept
+        * during assembly are set up here.
+        */
+#ifdef CPM
+       unlink("????????.$$$");
+       tfile=fopen("TFILE.$$$", "w");
+       dfile=fopen("DFILE.$$$", "w");
+       rtfile=fopen("RTFILE.$$$", "w");
+       rdfile=fopen("RDFILE.$$$", "w");
+#else
+       /*
+        * The function tmpfil() returns a file-descriptor
+        * of a file that is valid for reading and writing.
+        * It has the nice property of generating truly unique names.
+        */
+
+       tfile=fdopen(tmpfil(),"w") ;
+       dfile=fdopen(tmpfil(),"w") ;
+       rtfile=fdopen(tmpfil(),"w") ;
+       rdfile=fdopen(tmpfil(),"w") ;
+#endif
+}
+
+initproc() {
+
+       /*
+        * Called at the start of assembly of every procedure.
+        */
+
+       stat_t *prevstate ;
+
+       prevstate= pst_cast getarea(sizeof pstate) ;
+       *prevstate= pstate ;
+       pstate.s_prevstat= prevstate ;
+       pstate.s_curpro= prp_cast 0 ;
+       pstate.s_fline= lnp_cast 0 ;
+       pstate.s_fdata= l_data ;
+       pstate.s_locl = (locl_t (*)[])
+               getarea(LOCLABSIZE * sizeof (*(pstate.s_locl))[0]);
+       zero(chp_cast pstate.s_locl,
+               LOCLABSIZE * (unsigned) sizeof (*(pstate.s_locl))[0]);
+       if ( memflg>2 ) memuse() ;
+}
+
+endproc() {
+       /* Throw the contents of the line and local label table away */
+       register line_t *lnp1;
+       register locl_t *lbhead,*lbp,*lbp_next;
+       register kind ;
+       register stat_t *prevstate;
+
+       while ( lnp1= pstate.s_fline ) {
+               pstate.s_fline= lnp1->l_next ;
+               kind= lnp1->type1 ;
+               if ( kind>VALLOW ) kind=VALLOW ;
+               freearea((area_t)lnp1,(unsigned)linesize[kind]) ;
+       }
+       prevstate= pstate.s_prevstat ;
+       if ( prevstate!= pst_cast 0 ) {
+               for ( lbhead= *pstate.s_locl;
+                       lbhead<&(*pstate.s_locl)[LOCLABSIZE] ; lbhead++ ) {
+                       for ( lbp=lbhead; lbp!= lbp_cast 0; lbp= lbp_next ) {
+                               lbp_next= lbp->l_chain;
+                               freearea((area_t)lbp,(unsigned)sizeof *lbp) ;
+                       }
+               }
+               pstate= *prevstate ;
+               freearea((area_t)prevstate,(unsigned)sizeof *prevstate) ;
+       }
+}
+
+init_module() {
+
+       /*
+        * Called at the start of every module.
+        */
+
+       holbase  = 0;
+       line_num = 1;
+       mod_sizes = 0;
+}
+
+end_module() {
+
+       /*
+        * Finish a module.
+        * Work to be done is mainly forgetting of local names,
+        * and remembering of those that will live during assembly.
+        */
+
+       align(wordsize) ;
+       setmode(DATA_NUL);
+       dump(100);
+       enmd_pro();
+       enmd_glo();
+       if ( memflg ) memuse() ;
+}
+
+enmd_pro() {
+       register proc_t *p,*limit;
+
+       /*
+        * Check that all local procedures have been defined,
+        * and forget them immediately thereafter.
+        */
+
+       limit = &mprocs[oursize->n_mproc];
+       for (p=mprocs; p<limit; p++) {
+               if (p->p_name[0] == 0)
+                       continue;
+               if ((p->p_status&DEF)==0)
+                       error("undefined local procedure '%s'",p->p_name);
+       }
+       zero(chp_cast mprocs,(limit-mprocs)* (unsigned)sizeof *mprocs);
+
+       /* Clobber all flags indicating that external procedures
+        * were used in this module.
+        */
+
+       limit = &xprocs[oursize->n_xproc];
+       for (p=xprocs; p<limit; p++) {
+               p->p_status &= ~EXT ;
+       }
+}
+
+enmd_glo() {
+       register glob_t *mg,*xg,*limit;
+
+       /*
+        * Tougher then enmd_pro().
+        * Check all the symbols used in this module that are
+        * not to be forgotten immediately.
+        * A difficulty arises here:
+        *      In the tables textreloc[] and datareloc[]
+        *      pointers are used to identify the symbols concerned.
+        *      These pointers point into mglobs[].
+        *      Since at the end of assembly only the value of xglobs[]
+        *      is defined, these pointers have to be changed.
+        *      upd_reloc() takes care of this.
+        */
+
+       limit = &mglobs[oursize->n_mlab];
+       for ( mg = mglobs; mg < limit; mg++) {
+               if (mg->g_name[0] == 0)
+                       continue;
+               if ((mg->g_status&(EXT|DEF))==0)
+                       error("undefined local symbol '%s'",glostring(mg));
+               if ((mg->g_status&EXT)==0)
+                       continue;
+               xg = xglolookup(mg->g_name,ENTERING);
+               switch(xg->g_status&(EXT|DEF)) {
+               case 0:         /* new symbol */
+                       if((mg->g_status&DEF)==0)
+                               ++unresolved;
+                       break;
+               case EXT:       /* already used but not defined */
+                       if(mg->g_status&DEF) {
+                               --unresolved;
+                       }
+                       break;
+               }
+               xg->g_status |= mg->g_status;
+               if (mg->g_status&DEF)
+                       xg->g_val.g_addr = mg->g_val.g_addr;
+               else
+                       mg->g_val.g_gp = xg;        /* used by upd_reloc */
+       } /* up to the next symbol */
+       upd_reloc();
+       zero(chp_cast mglobs,(limit-mglobs)*(unsigned) sizeof *mglobs);
+}
+
+finish_up()
+{
+       /*
+        * Almost done. Check for unresolved references,
+        * make the e.out file and stop.
+        */
+
+#ifdef JOHAN
+       if ( jflag ) return ;
+#endif
+#ifdef DUMP
+       c_print();
+#endif
+       check_def();
+       if ( nerrors==0 ) copyout();
+}
+
+#ifdef DUMP
+c_print() {
+       if ( ! c_flag ) return ;
+       c_dprint("primary",opcnt1) ;
+       c_dprint("secondary",opcnt2) ;
+       c_dprint("extra long",opcnt3) ;
+}
+
+c_dprint(str,cnt) char *str,*cnt ; {
+       register int first,curr ;
+       printf("unused %s opcodes\n",str) ;
+       for ( first= -1 , curr=0 ; curr<=256 ; curr++ ) {
+               if ( curr==256 || cnt[curr]  ) {
+                       if ( first!= -1 ) {
+                               if ( first+1 == curr ) {
+                                       printf("%3d\n",first ) ;
+                               } else {
+                                       printf("%3d..%3d\n",first,curr-1) ;
+                               }
+                               first= -1 ;
+                       }
+               } else {
+                       if ( first== -1 ) first=curr ;
+               }
+       }
+}
+#endif
+
+check_def() {
+       register proc_t *p;
+       register glob_t *g;
+       register count;
+
+       /*
+        * Check for unresolved references.
+        * NOTE: The occurring of unresolved references is not fatal,
+        *       although the use of the e.out file after this
+        *       occurring must be strongly discouraged.
+        *       Every use of the symbols concerned is undefined.
+        */
+
+       if (unresolved) {
+               printf("Unresolved references\n  Procedures:\n");
+               count = oursize->n_xproc;
+               for (p = xprocs; count--; p++)
+                       if (p->p_name[0] && (p->p_status&DEF)==0)
+                               printf("    %s\n",p->p_name);
+               printf("  Data:\n");
+               count = oursize->n_glab;
+               for (g = xglobs; count--; g++)
+                       if (g->g_name[0] && (g->g_status&DEF)==0)
+                               printf("    %s\n",glostring(g));
+       }
+}
+
+ertrap() { /* trap routine to drain input in case of compile errors */
+
+       if (fileno(ifile)== 0)
+               while (fgetc(ifile) != EOF)
+                       ;
+       exit(1);
+}
diff --git a/util/ass/ass00.h b/util/ass/ass00.h
new file mode 100644 (file)
index 0000000..375ba97
--- /dev/null
@@ -0,0 +1,246 @@
+#include <stdio.h>
+#include "../../h/em_spec.h"
+#include "../../h/as_spec.h"
+#include "../../h/em_flag.h"
+#include "../../h/arch.h"
+#include "../../h/local.h"
+
+/*
+ * compile time options
+ */
+
+/*  #define DUMP            1       /* dump between passes */
+/*  #define TIMING          1       /* some timing measurements */
+/*  #define JOHAN           1       /* dump the loaded instructions */
+/*  #define MEMUSE          1       /* print memory usage statistics */
+
+#ifndef DUMP
+#define dump(x)                  /* nothing */
+#endif
+
+#ifndef TIMING
+#define timing()                /* nothing */
+#endif
+
+#ifndef MEMUSE
+#define memuse()                /* nothing */
+#endif
+
+/* Used to clear the upper byte(s) of characters.
+   Not nessecary if your C-compiler does not sign-extend char's
+*/
+
+#ifdef CPM
+# define        LC(ch)   ( ((ch)<'A' | (ch)>'Z' ) ? (ch) : ((ch)-('A'-'a')))
+#else
+# define        LC(ch)          (ch)
+#endif
+
+#define ctrunc(val)             ( (val)&0377 )
+
+#define odd(n)                  ((n)&1)         /* Boolean odd function */
+
+#define lnp_cast                (line_t *)
+#define gbp_cast                (glob_t *)
+#define lbp_cast                (locl_t *)
+#define prp_cast                (proc_t *)
+#define ptp_cast                (ptab_t *)
+#define rlp_cast                (relc_t *)
+#define pst_cast                (stat_t *)
+#define chp_cast                (char   *)
+#define ipp_cast                (int   **)
+#define iip_cast                (int    *)
+#define int_cast                (int     )
+
+typedef struct lines            line_t;
+typedef struct loc_label        locl_t;
+typedef struct glob_label       glob_t;
+typedef struct rel              relc_t;
+typedef struct procstat         stat_t;
+typedef struct sizes            size_t;
+typedef struct ar_hdr           arch_t;
+typedef struct procs            proc_t;
+typedef struct proctab          ptab_t;
+typedef char *                  area_t;
+typedef long                    cons_t;
+
+typedef union {
+       cons_t  ad_i;
+       locl_t  *ad_lp;
+       glob_t  *ad_gp;
+       proc_t  *ad_pp;
+       struct sad_ln {
+               short   ln_extra;
+               short   ln_first;
+       }       ad_ln ;
+       struct sad_df {
+               cons_t  df_i;
+               glob_t  *df_gp;
+       }       ad_df;
+}       addr_u;
+
+typedef union {
+       cons_t  rel_i;
+       locl_t  *rel_lp;
+       glob_t  *rel_gp;
+} rel_u;
+
+#define FOFFSET         long             /* offset into file */
+
+/*
+ * Global variables and definitions for EM1-assembler/loader
+ */
+
+#define DEFINING        0       /* parameters for glolookup */
+#define OCCURRING       1
+#define INTERNING       2
+#define EXTERNING       3
+#define SEARCHING       4
+#define ENTERING        5
+
+#define PRO_OCC         0       /* parameters for prolookup */
+#define PRO_DEF         1
+#define PRO_INT         2
+#define PRO_EXT         3
+
+#define TRUE            1
+#define FALSE           0
+
+#define IDLENGTH        8       /* length of glo's and pro's */
+#define MAXSTRING       200     /* Maximum string length accepted */
+#define LOCLABSIZE      128     /* size of local label hash table */
+                               /* may not be smaller */
+#define ABSSIZE         8
+
+struct  lines {
+       char    instr_num;      /* index into mnemon[] */
+       char    type1;          /* see below */
+       line_t  *l_next;        /* next in chain */
+       char    *opoff;         /* pointer into opchoice[] */
+       addr_u  ad;             /* depending on type, various pointers */
+};
+
+/* contents of type1 */
+#define MISSING         0       /* no operand */
+#define CONST           1       /* ad contains operand */
+#define PROCNAME        2       /* ad contains struct procs pointer */
+#define GLOSYM          3       /* ad contains pointer into mproc[] */
+#define LOCSYM          4       /* ad contains pointer into locs[] */
+#define GLOOFF          5       /* ad contains CONST and GLOSYM in ad_df */
+#define LINES           6       /* Line number setting, only param of pseudo*/
+#define VALLOW          7       /* value's between LOW and HIGH are x-MID */
+#define VALMID         50
+#define VALHIGH       127       /* to avoid sign extension problems */
+
+#define VAL1(x)         ((x)-VALMID)
+
+/* Used to indicate a invalid contents of opoff */
+#define NO_OFF          ((char *)-1)
+
+/* The structure containing procedure pertinent data */
+/* Used for environment stacking for nested PRO's */
+
+struct  procstat  {
+       line_t  *s_fline;       /* points to first line of procedure */
+       locl_t  (*s_locl)[];    /* pointer to local labels */
+       proc_t  *s_curpro;      /* identifies current procedure */
+       relc_t  *s_fdata;       /* last datareloc before procedure */
+       stat_t  *s_prevstat;    /* backward chain of nested procedures */
+} ;
+
+struct  loc_label {
+       locl_t  *l_chain;       /* The next label with same low order bits */
+       char    l_hinum;        /* high bits of number of label */
+       char    l_defined;      /* see below */
+       int     l_min,l_max;    /* boundaries of value */
+};
+
+/* contents of l_defined */
+#define EMPTY           0       /* Empty slot */
+#define NO              1       /* not defined yet */
+#define YES             2       /* defined */
+#define SEEN            3       /* intermediate state */
+#define NOTPRESENT      4       /* Undefined and error message given */
+
+struct  glob_label {
+       char    g_name[IDLENGTH+1];     /* name + null-byte */
+       char    g_status;               /* see below */
+       union {
+               cons_t  g_addr;         /* value if status&DEF */
+               struct glob_label *g_gp; /* ref. to xglobs */
+       } g_val ;
+};
+
+#define glostring(gl)   ((gl)->g_name)
+
+/* contents of g_status */
+#define DEF             01      /* defined */
+#define OCC             02      /* used */
+#define EXT             04      /* external */
+
+struct  rel {                   /* for relocation tables */
+       relc_t  *r_next;        /* chain */
+       FOFFSET r_off;          /* offset in text/data of word to relocate */
+       rel_u   r_val;          /* constant or pointer to global symbol */
+       int     r_typ;          /* different use in text or data */
+};
+
+/*
+ * When used with textrelocation r_typ contains the flag bits as defined
+ * in ip_spec.h together with the RELMNS bit if r_val contains an integer
+ */
+
+#define RELMNS          020000          /* indicates integer i.s.o. glob */
+
+/* Contents of r_typ when used with data relocation */
+#define RELNULL         0
+#define RELGLO          1
+#define RELHEAD         2
+#define RELLOC          3
+#define RELADR          4
+
+/* modes of data output */
+#define DATA_NUL        0
+#define DATA_REP        1
+#define DATA_CONST      2
+#define DATA_BSS        3
+#define DATA_DPTR       4
+#define DATA_IPTR       5
+#define DATA_ICON       6
+#define DATA_UCON       7
+#define DATA_FCON       8
+#define DATA_BYTES      9
+
+/* name of procedure to be called first */
+#define MAIN            "m_a_i_n"
+
+/* headers of datablocks written */
+#define HEADREP         0
+#define HEADBSS         1
+#define HEADBYTE        2
+#define HEADCONST       3
+#define HEADDPTR        4
+#define HEADIPTR        5
+#define HEADICON        6
+#define HEADUCON        7
+#define HEADFCON        8
+
+#define NDEFAULT        3       /* number of different sizes available */
+struct  sizes {
+       int     n_mlab;         /* # of global labels per module */
+       int     n_glab;         /* # of extern global labels */
+       int     n_mproc;        /* # of local procs per module */
+       int     n_xproc;        /* # of external procs */
+       int     n_proc;         /* total # of procedures */
+};
+
+struct  procs {                 /* format of mprocs[] and xprocs[] */
+       char    p_name[IDLENGTH+1];     /* name + 1 null-byte */
+       char    p_status;       /* same bits as g_status except REL */
+       int     p_num;          /* unique procedure descriptor */
+};
+
+struct  proctab {
+       cons_t  pr_off;         /* distance from pb */
+       cons_t  pr_loc;         /* number of bytes locals */
+};