]> Shamusworld >> Repos - rmac/blobdiff - symbol.c
Lots of fixes for floating point handling; version now at 1.11.0.
[rmac] / symbol.c
index 747c5a7e7099d655437e00c17f3bc2466e422fb1..b66216b6d3b9caaec155ba913f9386e713a90330 100644 (file)
--- a/symbol.c
+++ b/symbol.c
 //
-// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
+// RMAC - Reboot's Macro Assembler for all Atari computers
 // SYMBOL.C - Symbol Handling
-// Copyright (C) 199x Landon Dyer, 2011-2012 Reboot and Friends
+// Copyright (C) 199x Landon Dyer, 2011-2017 Reboot and Friends
 // RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
-// Source Utilised with the Kind Permission of Landon Dyer
+// Source utilised with the kind permission of Landon Dyer
 //
 
 #include "symbol.h"
+#include "error.h"
 #include "listing.h"
+#include "object.h"
 #include "procln.h"
-#include "error.h"
 
-static SYM *sytab[NBUCKETS];                                // User symbol-table header
-int curenv;                                                 // Current enviroment number
-SYM *sorder;                                                // * -> Symbols, in order of reference
-SYM *sordtail;                                              // * -> Last symbol in sorder list
-SYM *sdecl;                                                 // * -> Symbols, in order of declaration
-SYM *sdecltail;                                             // * -> Last symbol in sdecl list
 
-// Tags for marking symbol spaces
+// Macros
+#define NBUCKETS 256                           // Number of hash buckets (power of 2)
+
+static SYM * symbolTable[NBUCKETS];    // User symbol-table header
+int curenv;                                                    // Current enviroment number
+static SYM * sorder;                           // * -> Symbols, in order of reference
+static SYM * sordtail;                         // * -> Last symbol in sorder list
+static SYM * sdecl;                                    // * -> Symbols, in order of declaration
+static SYM * sdecltail;                                // * -> Last symbol in sdecl list
+static uint32_t currentUID;                    // Symbol UID tracking (done by NewSymbol())
+uint32_t firstglobal; // Index of the first global symbol in an ELF object.
+
+// Tags for marking symbol spaces:
 // a = absolute
 // t = text
 // d = data
 // ! = "impossible!"
 // b = BSS
-static char tdb_text[8] = {
+static uint8_t tdb_text[8] = {
    'a', 't', 'd', '!', 'b', SPACE, SPACE, SPACE
 };
 
+
 //
-// --- Initialize Symbol Table ---------------------------------------------------------------------
+// Initialize symbol table
 //
-
-void init_sym(void) {
-   int i;                                                   // Iterator
-
-   for(i = 0; i < NBUCKETS; ++i)                            // Initialise symbol hash table
-      sytab[i] = NULL;
-
-   curenv = 1;                                              // Init local symbol enviroment
-   sorder = NULL;                                           // Init symbol-reference list
-   sordtail = NULL;
-   sdecl = NULL;                                            // Init symbol-decl list
-   sdecltail = NULL;
+void InitSymbolTable(void)
+{
+       for(int i=0; i<NBUCKETS; i++)                   // Initialise symbol hash table
+               symbolTable[i] = NULL;
+
+       curenv = 1;                                                             // Init local symbol enviroment
+       sorder = NULL;                                                  // Init symbol-reference list
+       sordtail = NULL;
+       sdecl = NULL;                                                   // Init symbol-decl list
+       sdecltail = NULL;
+       currentUID = 0;
 }
 
+
 //
-// --- Allocate and Return Pointer to a Copy of a String -------------------------------------------
+// Hash the ASCII name and enviroment number
 //
-
-char *nstring(char *str) {
-   long i;
-   char *s, *d;
-
-   for(i = 0; str[i]; ++i)
-      ;
-   s = d = amem(i + 1);
-   while(*str)
-      *d++ = *str++;
-   *d++ = '\0';
-   
-   return(s);
+int HashSymbol(uint8_t * name, int envno)
+{
+       int sum = envno, k = 0;
+
+       for(; *name; name++)
+       {
+               if (k++ == 1)
+                       sum += *name << 2;
+               else
+                       sum += *name;
+       }
+
+       return sum & (NBUCKETS - 1);
 }
 
+
 //
-// --- Hash the Print Name and Enviroment Number ---------------------------------------------------
+// Make a new symbol of type 'type' in enviroment 'envno'
 //
-int syhash(char *name, int envno) {
-   int sum, k;                                              // Hash calculation
-
-   k = 0;
-   for(sum = envno; *name; ++name) {
-      if(k++ == 1)
-         sum += *name << 2;
-      else
-         sum += *name;
-   }
-
-   return(sum & (NBUCKETS - 1));
+SYM * NewSymbol(uint8_t * name, int type, int envno)
+{
+       // Allocate the symbol
+       SYM * symbol = malloc(sizeof(SYM));
+
+       if (symbol == NULL)
+       {
+               printf("NewSymbol: MALLOC ERROR (symbol=\"%s\")\n", name);
+               return NULL;
+       }
+
+       // Fill-in the symbol
+       symbol->sname  = strdup(name);
+       symbol->stype  = (uint8_t)type;
+       symbol->senv   = (uint16_t)envno;
+       // We don't set this as DEFINED, as it could be a forward reference!
+       symbol->sattr  = 0;
+       // We don't set RISCSYM here as not every symbol first seen in a RISC
+       // section is a RISC symbol!
+       symbol->sattre = 0;
+       symbol->svalue = 0;
+       symbol->sorder = NULL;
+       symbol->uid    = currentUID++;
+
+       // Install symbol in the symbol table
+       int hash = HashSymbol(name, envno);
+       symbol->snext = symbolTable[hash];
+       symbolTable[hash] = symbol;
+
+       // Append symbol to the symbol-order list
+       if (sorder == NULL)
+               sorder = symbol;                                        // Add first symbol
+       else
+               sordtail->sorder = symbol;                      // Or append to tail of list
+
+       sordtail = symbol;
+       return symbol;
 }
 
+
 //
-// --- Make a new symbol of type `type' in enviroment `envno' --------------------------------------
+// Look up the symbol name by its UID and return the pointer to the name.
+// If it's not found, return NULL.
 //
-
-SYM *newsym(char *name, int type, int envno) {
-   int hash;                                                // Symbol hash value
-   SYM *sy;                                                 // Pointer to symbol
-
-   
-   // Allocate the symbol
-   sy = (SYM *)amem((long)(sizeof(SYM)));
-   if(sy == NULL) {
-      printf("SYMALLOC ERROR (%s)\n", name);
-      return(NULL);
-   }
-
-   sy->sname = nstring(name);
-
-   // Fill-in the symbol
-   sy->stype  = (BYTE)type;
-   sy->senv   = (WORD)envno;
-   sy->sattr  = 0;
-   if(rgpu || rdsp) sy->sattre = RISCSYM;
-   else sy->sattre = 0;
-   sy->svalue = 0;
-
-   // Install symbol in symbol table
-   hash = syhash(name, envno);
-   sy->snext = sytab[hash];
-   sytab[hash] = sy;
-
-   // Append symbol to symbol-order list
-   if(sorder == NULL)
-      sorder = sy;                                          // Add first symbol 
-   else
-      sordtail->sorder = sy;                                // Or append to tail of list
-
-   sy->sorder = NULL;
-   sordtail = sy;
-
-   return(sy);                                              // Return pointer to symbol
+uint8_t * GetSymbolNameByUID(uint32_t uid)
+{
+       //problem is with string lookup, that's why we're writing this
+       //so once this is written, we can put the uid in the token stream
+
+       // A much better approach to the symbol order list would be to make an
+       // array--that way you can do away with the UIDs and all the rest, and
+       // simply do an array lookup based on position. But meh, let's do this for
+       // now until we can rewrite things so they make sense.
+       SYM * symbol = sorder;
+
+       for(; symbol; symbol=symbol->sorder)
+       {
+               if (symbol->uid == uid)
+                       return symbol->sname;
+       }
+
+       return NULL;
 }
 
+
 //
-// --- Lookup the symbol `name', of the specified type, with the specified enviroment level --------
+// Lookup the symbol 'name', of the specified type, with the specified
+// enviroment level
 //
-
-SYM *lookup(char *name, int type, int envno) {
-   SYM *sy;                                                 // Symbol record pointer
-   int k, sum;                                              // Hash bucket calculation
-   char *s;                                                 // String pointer
-
-   // Pick a hash-bucket (SAME algorithm as syhash())
-   k = 0;
-   s = name;
-   for(sum = envno; *s;) {
-      if(k++ == 1)
-         sum += *s++ << 2;
-      else sum += *s++;
-   }
-
-   sy = sytab[sum & (NBUCKETS-1)];
-
-   // Do linear-search for symbol in bucket
-   while(sy != NULL) {
-      if(sy->stype == type       &&                         // Type, envno and name must match
-         sy->senv  == envno      &&
-                       *name     == *sy->sname &&                         // Fast check for first character
-                       !strcmp(name, sy->sname))
-         break;
-      else sy = sy->snext;
-   }
-
-   return(sy);                                              // Return NULL or matching symbol
+SYM * lookup(uint8_t * name, int type, int envno)
+{
+       SYM * symbol = symbolTable[HashSymbol(name, envno)];
+
+       // Do linear-search for symbol in bucket
+       while (symbol != NULL)
+       {
+               if (symbol->stype == type                       // Type, envno and name must match
+                       && symbol->senv  == envno
+                       && *name == *symbol->sname              // Fast check for first character
+                       && !strcmp(name, symbol->sname))        // More expensive check
+                       break;
+
+               symbol = symbol->snext;
+       }
+
+       // Return NULL or matching symbol
+       return symbol;
 }
 
+
 //
-// --- Put symbol on "order-of-declaration" list of symbols ----------------------------------------
+// Put symbol on "order-of-declaration" list of symbols
 //
-
-void sym_decl(SYM *sym) {
-   if(sym->sattr & SDECLLIST) return;                       // Already on list
-   sym->sattr |= SDECLLIST;                                 // Mark "already on list"
-
-   if(sdecl == NULL)
-      sdecl = sym;                                          // First on decl-list
-   else 
-      sdecltail->sdecl = sym;                               // Add to end of list
-
-   sym->sdecl = NULL;                                       // Fix up list's tail
-   sdecltail = sym;
+void AddToSymbolDeclarationList(SYM * symbol)
+{
+       // Don't add if already on list, or it's an equated register/CC
+       if ((symbol->sattr & SDECLLIST)
+               || (symbol->sattre & (EQUATEDREG | UNDEF_EQUR | EQUATEDCC | UNDEF_CC)))
+               return;
+
+       // Mark as "on .sdecl list"
+       symbol->sattr |= SDECLLIST;
+
+       if (sdecl == NULL)
+               sdecl = symbol;                         // First on decl-list
+       else
+               sdecltail->sdecl = symbol;      // Add to end of list
+
+       // Fix up list's tail
+       symbol->sdecl = NULL;
+       sdecltail = symbol;
 }
 
+
 //
-// --- Make all referenced, undefined symbols global -----------------------------------------------
+// Make all referenced, undefined symbols global
 //
+void ForceUndefinedSymbolsGlobal(void)
+{
+       SYM * sy;
+
+       DEBUG printf("~ForceUndefinedSymbolsGlobal()\n");
+
+       // Scan through all symbols; if a symbol is REFERENCED but not DEFINED,
+       // then make it global.
+       for(sy=sorder; sy!=NULL; sy=sy->sorder)
+       {
+               if (sy->stype == LABEL && sy->senv == 0
+                       && ((sy->sattr & (REFERENCED | DEFINED)) == REFERENCED))
+                       sy->sattr |= GLOBAL;
+       }
+}
 
-int syg_fix(void) {
-   SYM *sy;
-
-   DEBUG printf("~syg_fix()\n");
-
-   // Scan through all symbols;
-   // If a symbol is REFERENCED but not DEFINED, then make it global.
-   for(sy = sorder; sy != NULL; sy = sy->sorder)
-      if(sy->stype == LABEL && sy->senv == 0 &&
-                       ((sy->sattr & (REFERENCED|DEFINED)) == REFERENCED))
-         sy->sattr |= GLOBAL;
 
-   return(0);
+//
+// Assign numbers to symbols that are to be exported or imported. The symbol
+// number is put in 'senv'. Returns the number of symbols that will be in the
+// symbol table.
+//
+// N.B.: This is usually called twice; first time with NULL parameters and the
+//       second time with real ones. The first one is typically done to get a
+//       count of the # of symbols in the symbol table, and the second is to
+//       actually create it.
+//
+uint32_t sy_assign(uint8_t * buf, uint8_t *(* construct)())
+{
+       uint16_t scount = 0;
+
+       // Done only on first pass...
+       if (buf == NULL)
+       {
+               // Append all symbols not appearing on the .sdecl list to the end of
+               // the .sdecl list
+               for(SYM * sy=sorder; sy!=NULL; sy=sy->sorder)
+                       AddToSymbolDeclarationList(sy);
+       }
+
+       // Run through all symbols (now on the .sdecl list) and assign numbers to
+       // them. We also pick which symbols should be global or not here.
+       for(SYM * sy=sdecl; sy!=NULL; sy=sy->sdecl)
+       {
+               // Export or import external references, and export COMMON blocks.
+               if ((sy->stype == LABEL)
+                       && ((sy->sattr & (GLOBAL | DEFINED)) == (GLOBAL | DEFINED)
+                       || (sy->sattr & (GLOBAL | REFERENCED)) == (GLOBAL | REFERENCED))
+                       || (sy->sattr & COMMON))
+               {
+                       sy->senv = scount++;
+
+                       if (buf != NULL)
+                               buf = construct(buf, sy, 1);
+               }
+               // Export vanilla labels (but don't make them global). An exception is
+               // made for equates, which are not exported unless they are referenced.
+               else if (sy->stype == LABEL && lsym_flag
+                       && (sy->sattr & (DEFINED | REFERENCED)) != 0
+                       && (!as68_flag || *sy->sname != 'L'))
+               {
+                       sy->senv = scount++;
+
+                       if (buf != NULL)
+                               buf = construct(buf, sy, 0);
+               }
+       }
+
+       return scount;
 }
 
+
 //
-// --- Convert string to uppercase -----------------------------------------------------------------
+// Custom version of sy_assign for ELF .o files.
+// The order that the symbols should be dumped is different.
+// (globals must be explicitly at the end of the table)
 //
-
-int uc_string(char *s) {
-   for(; *s; ++s)
-      if(*s >= 'a' && *s <= 'z')
-         *s -= 32;
-   return(0);
+// N.B.: It should be possible to merge this with sy_assign, as there's nothing
+//       really ELF specific in here, other than the "globals go at the end of
+//       the queue" thing, which doesn't break the others. :-P
+uint32_t sy_assign_ELF(uint8_t * buf, uint8_t *(* construct)())
+{
+       uint16_t scount = 0;
+
+//     if (construct == (uint8_t *(*)())constr_elfsymtab)
+//     if (buf == NULL)
+       {
+               // Append all symbols not appearing on the .sdecl list to the end of
+               // the .sdecl list
+               for(SYM * sy=sorder; sy!=NULL; sy=sy->sorder)
+                       AddToSymbolDeclarationList(sy);
+       }
+
+       // Run through all symbols (now on the .sdecl list) and assign numbers to
+       // them. We also pick which symbols should be global or not here.
+       for(SYM * sy=sdecl; sy!=NULL; sy=sy->sdecl)
+       {
+               // Export vanilla labels (but don't make them global). An exception is
+               // made for equates, which are not exported unless they are referenced.
+               if (sy->stype == LABEL && lsym_flag
+                       && (sy->sattr & (DEFINED | REFERENCED)) != 0
+                       && (*sy->sname != '.')
+                       && (sy->sattr & GLOBAL) == 0)
+               {
+                       sy->senv = scount++;
+
+                       if (buf != NULL)
+                               buf = construct(buf, sy, 0);
+               }
+       }
+
+       firstglobal = scount;
+
+       // For ELF object mode run through all symbols in reference order
+       // and export all global-referenced labels. Not sure if this is
+       // required but it's here nonetheless
+
+       //for(sy=sdecl; sy!=NULL; sy=sy->sorder)
+       for(SYM * sy=sdecl; sy!=NULL; sy=sy->sdecl)
+       {
+               if ((sy->stype == LABEL)
+                       && ((sy->sattr & (GLOBAL | DEFINED)) == (GLOBAL | DEFINED)
+                       || (sy->sattr & (GLOBAL | REFERENCED)) == (GLOBAL | REFERENCED))
+                       || (sy->sattr & COMMON))
+               {
+                       sy->senv = scount++;
+
+                       if (buf != NULL)
+                               buf = construct(buf, sy, 1);
+               }
+               else if ((sy->sattr == (GLOBAL | REFERENCED)) &&  (buf != NULL))
+               {
+                       buf = construct(buf, sy, 0);
+                       scount++;
+               }
+       }
+
+       return scount;
 }
 
+
 //
-// -------------------------------------------------------------------------------------------------
-// Assign numbers to symbols that are to be exported or imported.  The symbol number is put in 
-// `.senv'. Return the number of symbols that will be in the symbol table.
-// -------------------------------------------------------------------------------------------------
+// Convert string to uppercase
 //
-
-int sy_assign(char *buf, char *(*constr)()) {
-   SYM *sy;
-   int scount;
-   //int i;
-
-   scount = 0;
-
-   if(buf == NULL)
-      // Append all symbols not appearing on the .sdecl list to the end of the .sdecl list
-      for(sy = sorder; sy != NULL; sy = sy->sorder) {
-
-         // Essentially the same as 'sym_decl()' above:
-         if(sy->sattr & SDECLLIST) continue;                // Already on list 
-         sy->sattr |= SDECLLIST;                            // Mark "on the list"
-
-         if(sdecl == NULL) sdecl = sy;                      // First on decl-list 
-         else sdecltail->sdecl = sy;                        // Add to end of list
-
-         sy->sdecl = NULL;                                  // Fix up list's tail
-         sdecltail = sy;
-      }
-
-   // Run through all symbols (now on the .sdecl list) and assign numbers to them.  We also pick 
-   // which symbols should be global or not here.
-   for(sy = sdecl; sy != NULL; sy = sy->sdecl) {
-
-      if(sy->sattre & UNDEF_EQUR) continue;                 // Don't want undefined on our list
-      if(sy->sattre & UNDEF_CC) continue;                   
-      
-      // Export or import external references, and export COMMON blocks.
-      if((sy->stype == LABEL) &&
-         ((sy->sattr & (GLOBAL|DEFINED)) == (GLOBAL|DEFINED) ||
-         (sy->sattr & (GLOBAL|REFERENCED)) == (GLOBAL|REFERENCED)) ||
-         (sy->sattr & COMMON)) {
-         sy->senv = (WORD)scount++;
-         if(buf != NULL) buf = (*constr)(buf, sy, 1);
-      } else
-         // Export vanilla labels (but don't make them global). An exception is made for equates, 
-         // which are not exported unless they are referenced.
-         if(sy->stype == LABEL && lsym_flag &&
-            (sy->sattr & (DEFINED|REFERENCED)) != 0 &&
-            (!as68_flag || *sy->sname != 'L') ) {
-            sy->senv = (WORD)scount++;
-            if(buf != NULL) buf = (*constr)(buf, sy, 0);
-         }
-   }
-
-   return(scount);
+void ToUppercase(uint8_t * s)
+{
+       for(; *s; s++)
+       {
+               if (*s >= 'a' && *s <= 'z')
+                       *s -= 0x20;
+       }
 }
 
+
 //
-// --- Generate symbol table for listing file ------------------------------------------------------
+// Generate symbol table for listing file
 //
-
-int symtable(void) {
-   int i;
-   int j;
-   SYM *q = NULL;
-   SYM *p;
-   SYM *r;
-   SYM *k;
-   SYM **sy;
-   SYM *colptr[4];
-   char ln[150];
-   char ln1[150];
-   char ln2[20];
-   char c, c1;
-   WORD w;
-   int ww;
-   int colhei;
-   extern int pagelen;
-
-   colhei = pagelen - 5;
-
-   // Allocate storage for list headers and partition all labels.  
-   // Throw away macros and macro arguments.
-   sy = (SYM **)amem((LONG)(128 * sizeof(LONG)));
-   for(i = 0; i < 128; ++i) sy[i] = NULL;
-
-   for(i = 0; i < NBUCKETS; ++i)
-      for(p = sytab[i]; p != NULL; p = k) {
-         k = p->snext;
-         j = *p->sname;
-         r = NULL;
-         if(p->stype != LABEL) continue;                    // Ignore non-labels
-         if(p->sattre & UNDEF_EQUR) continue;
-
-         for(q = sy[j]; q != NULL; q = q->snext)
-            if(strcmp(p->sname, q->sname) < 0)
-               break;
-            else r = q;
-
-         if(r == NULL) {                                    // Insert at front of list
-            p->snext = sy[j];
-            sy[j] = p;
-         } else {                                           // Insert in middle or append to list
-            p->snext = r->snext;
-            r->snext = p;
-         }
-      }
-
-   // Link all symbols onto one list again
-   p = NULL;
-   for(i = 0; i < 128; ++i)
-      if((r = sy[i]) != NULL) {
-         if(p == NULL)
-            q = r;
-         else q->snext = r;
-
-         while(q->snext != NULL)
-            q = q->snext;
-
-         if(p == NULL)
-            p = r;
-      }
-
-   eject();
-   strcpy(subttl, "Symbol Table");
-
-   while(p != NULL) {
-      for (i = 0; i < 4; ++i) {
-         colptr[i] = p;
-         for(j = 0; j < colhei; ++j)
-            if(p == NULL)
-               break;
-            else p = p->snext;
-      }
-
-      for(i = 0; i < colhei; ++i) {
-         *ln = EOS;
-         if(colptr[0] == NULL)
-            break;
-
-         for(j = 0; j < 4; ++j) {
-            if((q = colptr[j]) == NULL)
-               break;
-            colptr[j] = q->snext;
-            w = q->sattr;
-            ww = q->sattre;
-            // Pick a tag:
-            // c       common
-            // x       external reference
-            // g       global (export)
-            // space   nothing special
-            c1 = SPACE;
-            c = SPACE;
-
-            if(w & COMMON) c = 'c';
-            else if((w & (DEFINED|GLOBAL)) == GLOBAL) c = 'x';
-            else if(w & GLOBAL) c = 'g';
-
-            c1 = tdb_text[w & TDB];
-            if(c == 'x') strcpy(ln2, "external");
-            else {
-               sprintf(ln2, "%08ux", q->svalue);
-               uc_string(ln2);
-            }
-
-            sprintf(ln1, "  %16s %s %c%c%c", q->sname, ln2, (ww & EQUATEDREG) ? 'e' : SPACE, c1, c);
-
-            strcat(ln, ln1);
-         }
-         ship_ln(ln);
-      }
-      eject();
-   }
-
-   return(0);
+int symtable(void)
+{
+       int i;
+       int j;
+       SYM * q = NULL;
+       SYM * p;
+       SYM * r;
+       SYM * k;
+       SYM * colptr[4];
+       char ln[1024];
+       char ln1[1024];
+       char ln2[20];
+       char c, c1;
+       WORD w;
+       int ww;
+       int colhei = pagelen - 5;
+
+       // Allocate storage for list headers and partition all labels. Throw away
+       // macros and macro arguments.
+       SYM ** sy = (SYM **)malloc(128 * sizeof(SYM **));
+
+       for(i=0; i<128; i++)
+               sy[i] = NULL;
+
+       for(i=0; i<NBUCKETS; i++)
+       {
+               for(p=symbolTable[i]; p!=NULL; p=k)
+               {
+                       k = p->snext;
+                       j = *p->sname;
+                       r = NULL;
+
+                       // Ignore non-labels
+                       if ((p->stype != LABEL) || (p->sattre & UNDEF_EQUR))
+                               continue;
+
+                       for(q=sy[j]; q!=NULL; q=q->snext)
+                       {
+                               if (strcmp(p->sname, q->sname) < 0)
+                                       break;
+
+                               r = q;
+                       }
+
+                       if (r == NULL)
+                       {
+                               // Insert at front of list
+                               p->snext = sy[j];
+                               sy[j] = p;
+                       }
+                       else
+                       {
+                               // Insert in middle or append to list
+                               p->snext = r->snext;
+                               r->snext = p;
+                       }
+               }
+       }
+
+       // Link all symbols onto one list again
+       p = NULL;
+
+       for(i=0; i<128; ++i)
+       {
+               if ((r = sy[i]) != NULL)
+               {
+                       if (p == NULL)
+                               q = r;
+                       else
+                               q->snext = r;
+
+                       while (q->snext != NULL)
+                               q = q->snext;
+
+                       if (p == NULL)
+                               p = r;
+               }
+       }
+
+       eject();
+       strcpy(subttl, "Symbol Table");
+
+       while (p != NULL)
+       {
+               for(i=0; i<4; i++)
+               {
+                       colptr[i] = p;
+
+                       for(j=0; j<colhei; j++)
+                       {
+                               if (p == NULL)
+                                       break;
+                               else
+                                       p = p->snext;
+                       }
+               }
+
+               for(i=0; i<colhei; i++)
+               {
+                       *ln = EOS;
+
+                       if (colptr[0] == NULL)
+                               break;
+
+                       for(j=0; j<4; j++)
+                       {
+                               if ((q = colptr[j]) == NULL)
+                                       break;
+
+                               colptr[j] = q->snext;
+                               w = q->sattr;
+                               ww = q->sattre;
+                               // Pick a tag:
+                               // c    common
+                               // x    external reference
+                               // g    global (export)
+                               // space        nothing special
+                               c1 = SPACE;
+                               c = SPACE;
+
+                               if (w & COMMON)
+                                       c = 'c';
+                               else if ((w & (DEFINED | GLOBAL)) == GLOBAL)
+                                       c = 'x';
+                               else if (w & GLOBAL)
+                                       c = 'g';
+
+                               c1 = tdb_text[w & TDB];
+
+                               if (c == 'x')
+                                       strcpy(ln2, "external");
+                               else
+                               {
+                                       sprintf(ln2, "%016lX", q->svalue);
+                                       ToUppercase(ln2);
+                               }
+
+                               sprintf(ln1, "  %16s %s %c%c%c", q->sname, ln2, (ww & EQUATEDREG) ? 'e' : SPACE, c1, c);
+                               strcat(ln, ln1);
+                       }
+
+                       ship_ln(ln);
+               }
+
+               eject();
+       }
+
+       return 0;
 }
+