Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Tools

A Generic One-Pass Assembler


SEP90: A GENERIC ONE-PASS ASSEMBLER

A GENERIC ONE-PASS ASSEMBLER

Symbol management is the key to one-pass assembly

This article contains the following executables: IVES.ARC

William E. Ives

William is a software design engineer in the Colorado Telecommunications division of Hewlett Packard, 5070 Centennial Blvd., Colorado Springs, CO 80919. William's Internet address is [email protected].


The one thing most programmers are interested in when developing code in assembly language is fast assembly turnaround time. A one-pass assembler facilitates this by avoiding the delays associated with multipass assemblers, and thus is able to out-perform them. This performance increase has some cost, the foremost being optimization. But even this can be minimized by proper symbol management.

This article describes one approach to a generic one-pass assembly. The ideas presented can be applied to most of the current assembly languages.

Symbols

A symbol is an alphanumeric shorthand used by programmers to reference a value that the symbol is tied to at some time during assembly. As an example, consider the symbol THERE in the following code fragment:

        JMP THERE

  ...   THERE ADD #1,DO

The assembler must interpret the value of THERE as being the address of the ADD instruction. It must then generate the machine code for the JMP instruction with the resolved address. Notice that #1 and DO are not considered symbols because their values or meanings are implied within the assembly language. In this case #1 means the immediate value of one, and D0 means data register zero.

As shown above, a symbol can be tied to an address by a line label. Most assemblers also provide a method of defining symbols through an assembler EQUATE pseudocommand.

  ONE EQUATE 1

Here, the EQUATE does not assemble to any machine code, but rather instructs the assembler to tie the value 1 to the symbol ONE.

Once the symbols are either tied to values or resolved, the job of the assembler is to use these values to generate machine code for those instructions whose operands reference symbols. For example, the JMP instruction shown earlier would normally have a base opcode followed by a target address within its machine code. The assembler fills in the target address portion of the machine code once the symbol THERE is resolved.

Forward References

Assemblers traditionally pass through code using a location counter to keep track of the address of each instruction. As each instruction is processed, the location counter is incremented based upon the number of bytes required to hold the machine code of the instruction. This forces the sequential location of machine code for each instruction with no "undefined" bytes between instructions. In addition, it leaves space in the final machine code to go back and fill in resolved symbols such as THERE.

This is a problem, however, when the amount of space to be reserved for forward referenced symbols is unknown. This is especially apparent on processors that offer multiple forms of the same instruction. For example, the JMP instruction might come in either form: 16-bit or 32-bit target address. The question is how much space is left in the machine code if the assembler does not yet know the value of the target address (THERE).

Multipass assemblers address this dilemma by resolving symbols on the first pass through, adjusting instruction addresses appropriately, then generating machine code on additional passes. With this approach, most multipass assemblers handle both the forward references and machine code optimization. Although this method generates optimal machine code, it is often time-consuming and thus tedious for the programmer who is looking for a fast turnaround time.

A one-pass assembler addresses this same problem by passing through the code only once, leaving space for the forward referenced symbols, then filling in these spaces as the symbols are resolved. This process is called "back-patching." By making the assembly language deterministic with assembler defaults or directives, the one-pass assembler knows how much space to leave for forward referenced symbols. This may not result in optimal code, but does result in improved assembly time.

The directives or defaults needed to make this possible depend heavily on the actual processor and its addressing modes. For example, the 6809 constant offset program counter for the relative mode has an 8-bit and a 16-bit form. This can be made deterministic by defaulting to the larger size (16-bit) and allowing the user to force the 8-bit offset by using an assembler directive (SHORT). The code in Figure 1 demonstrates this.

Figure 1: Example of the SHORT directive to generate an 8-bit offset

  0000  A6 8D 0003       LDA FORWARD, PCR ; 16-bit offset
                         SHORT
  0004  A6 8C 00         LDA FORWARD, PCR ; 8-bit offset
  0007  01     FORWARD   FCB 1  ;Force constant byte

An example for the 68000 involves the absolute addressing mode, which can be either a 16-bit or 32-bit address. Whenever a forward referenced symbol is used for these modes, the current assembler default is used. This default size can be changed from word to long by the directives shown in Figure 2.

Figure 2: Changing the default addressing mode on the 68000

                    ABS_SHORT               ;set default to absolute word
  0000  2038  000A  MOVE.B DO, LABEL
                    ABS_LONG                ;set default to absolute long
  0004  2039  0000  MOVE.B DO, LABEL
        000A
  000A  01      LABEL DC.B 1 ;define constant byte

The 68000 branch commands must also be deterministic because they come in byte and word forms. This is done by defaulting to the word offset unless the instruction is qualified by a size specifier of .B, as shown in Figure 3.

Figure 3: Making a branch command deterministic using the .B size specifier

  0000  6004               BRA.B  TARGET  ;8 bit offset of 4
  0002  6000  0002         BRA    TARGET  ;16 bit offset of 2
  0006  6000  FFFE TARGET  BRA    TARGET  ;16 bit offset of -2

These limitations might seem unreasonable at first, but the default assembler settings can be used so that minimal effort is required on the part of the programmer. The programmer can use the directives to override the defaults for optimization; this need be done only for forward references because backward references can automatically be optimized. This approach is not at all uncommon in many of today's commercial assemblers. For example, those familiar with Microsoft's Macro Assembler will recognize the directive, jmp short there, which tells the assembler that the jump reference is within the current code segment.

Symbol Management

Once an assembly language is made deterministic, symbol management becomes the key to the assembly process. First, each line is parsed, and any symbols are extracted from it. Then a machine code size determination is made based on assembler defaults and directives. If all of an instruction's operand symbols are resolved, the machine code can be immediately generated. Otherwise, all information associated with the instruction and its unresolved symbols is saved (in an operand reference list) for later assembly through backpatching. In either case, the location counter is incremented by the size of the machine code, and the next source line is similarly processed.

The symbols for each line are processed immediately after they are parsed from the line. The two types of symbols are the line label and any operand symbols. Each type of symbol is associated with a list: A label with the resolved value list and an operand symbol with the reference list. The line label is resolved to the current value of the location counter. The label and its value are added to the label list so that future references to the label can be resolved. Then, any previous references to the line label are taken from the operand reference list. These are resolved, and machine code generation is done for each of their instructions through backpatching. A flowchart of this is shown in Figure 4.

The operand symbol management was described earlier. A flowchart for this is shown in Figure 5. An example of assembling a generic piece of assembly code using these two flowcharts is shown in Figure 6.

Note that at any given time the operand reference lists contain only those forward references not yet resolved. In this respect, it does rely on memory being available for expansion, but the list size is always at a minimum for the code being assembled. Again, this is the trade-off of space versus time.

Multiple Modules

The above management model can easily be expanded to support multiple module assembly. This is done by introducing a global directive to export symbols from a module and an external directive to import symbols. In addition, two new lists are added: A global label list and an external reference list. These are similar to the label list and operand reference list, respectively.

The new flowcharts are shown in Figure 7 and Figure 8. The major difference to note in the label symbol management flowchart (Figure 8) is the order in which the various label lists are searched. By searching the external and global lists first, this flowchart precludes the possibility of mistakenly placing a global label in the local label list. This order of searching also provides the scoping rules between local and global symbols.

The operand symbol management flowchart (Figure 7) shows another point of interest. If a global label has not been resolved before it is referenced as an operand symbol, the reference is added to the local operand reference list. This is done because a global symbol must be resolved before the end of a module. Thus, any references to it can be treated just like local references.

Linking

As with any multiple module assembly, a linker must be used to create the final assembly program. Here, again, the one-pass symbol management approach is used because linking is just a matter of resolving external references across modules and generating machine code through backpatching. Of course, linking involves much more, but a detailed discussion is beyond the scope of this article.

One feature of linking is relocation, which is not too difficult to add to the current model because it merely involves treating all symbols as relative to the base of the module. Once the module base address is known, all symbols for the module are resolved. Another advanced feature is arithmetic expression support. This can be added by using expression trees in place of the symbols. Resolution then becomes the resolution of the entire tree for each instruction's operand. Unfortunately, each of these adds overhead and slows down the assembler. If these advanced features are used extensively, the trade-off may be worthwhile.

A One-Pass Assembler

Listings One through Nine provide a simple one-pass assembler for a generic assembly language. Listing One (page 92) provides the main driver; Listing Two (page 92) contains the procedures to handle assembly; Listing Three (page 96) is the error handler module; Listing Four (page 97) contains the procedures to handle the instruction set; Listing Five (page 98) is the listing module; Listing Six (page 100) is the math module, Listing Seven (page 100) is the parser module; Listing Eight (page 102) provides the procedures for pseudocommand assembly; and Listing Nine (page 103) is the symbol table module.

Because of space considerations, all comments have been removed from the code. Fully commented code, further test cases, and an executable version of the assembler are available from DDJ. (For more information see page 3.)

This code can easily be modified to build a full-featured assembler by adding an advanced parser, instruction opcode lookup facility, and error trapping. In all, symbol management is the key to one-pass assembly. And, although such an assembler may not create optimal code, it does have the fastest development turnaround time.

_A GENERIC ONE-PASS ASSEMBLER_ by William E. Ives

[LISTING ONE]

<a name="01d8_000e">


/**************************************************************************

  Main driver for generic assembler.
  Copyright 1988 by Michigan Technological University

  Written by : William E. Ives

  Version : 1.0
  Date     : Feb 1, 1989

 **************************************************************************/

#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <time.h>
#include <dir.h>

#include "68defs.h"
#include "68err.h"
#include "68parse.h"
#include "68list.h"
#include "68assem.h"
#include "68symtab.h"

void assembler_print_errors ( char * message , char * add_mess )
{
 printf(" %s %s \n",message,add_mess );
}

main()
{
 int     error_count, warning_count ;
 FILE   * outfile ;
 FILE   * in_file ;
 char     fn[MAXPATH],outname[MAXPATH], temp[MAXPATH], *ptr;

 /* Following used to parse a path into its components. */
 char drive[MAXDRIVE] , dir[MAXDIR], file[MAXFILE], ext[MAXEXT];

 error_count = 0 ;
 warning_count = 0 ;

 e_printf = assembler_print_errors ;

 puts(" Generic Assembler. Version 1.0\n");
 puts(" Written by : William E. Ives");
 puts(" Copyright (c) 1988 by Michigan Technological University.\n");

 printf(" Absolute or Relative assembly ? (A/R) ");
 fn[0] = getche();
 putchar('\n');
 am_assem_class = (( fn[0] == 'a')||(fn[0] == 'A')) ?
        am_absolute : am_relative ;

 printf(" Enter source file name [.ASM] =>");
 fn[0]=MAXPATH-1;
 ptr=cgets(fn);
 strcpy(fn,ptr);
 putchar('\n');

 fnsplit( fn,drive,dir,file,ext);

 /* assign list file name.*/

 if ( ! ( *ext ) )
    fnmerge( fn, drive,dir,file,".ASM");

 fnmerge( outname,drive,dir,file,".LST");

 printf(" Enter name of list file [%s] =>",outname);
 temp[0]=MAXPATH-1;
 ptr = cgets(temp);
 putchar('\n');
 if ( temp[1] ) strcpy(outname,ptr);

 in_file = fopen( fn,"r");
 if ( in_file == NULL ) {
    e_message(0,30, fn );
    return 30 ;
    }

 puts(" Assembling..");

 e_hold_messages = TRUE ;
 am_pass1( in_file , &error_count, &warning_count );
 e_hold_messages = FALSE ;

 fclose(in_file);

 printf(" Total Errors %d    Total Warnings %d \n",
     error_count, warning_count );

 puts(" Writing listing file.\n");
 outfile = fopen( outname,"w");
 if ( outfile == NULL ) {
    e_message(0,30, outname );
    return 30 ;
    }
 l_printlisting( outfile ,TRUE );
 fprintf(outfile,"\n Total Errors %d    Total Warnings %d \n",
       error_count, warning_count );

 fprintf(outfile,"\n\n Name of file :%s\n",fn);
 fclose(outfile);

 return 0 ;

} /* main */




<a name="01d8_000f"><a name="01d8_000f">
<a name="01d8_0010">
[LISTING TWO]
<a name="01d8_0010">


/*
     68000 Assembly Module.
    This module contains those procedures needed to handle
    assembly.
*/

  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <ctype.h>

  #include "68defs.h"
  #include "68err.h"
  #include "68parse.h"
  #include "68list.h"
  #include "68assem.h"
  #include "68symtab.h"
  #include "68pseudo.h"
  #include "68instr.h"

  #define LINELEN 80
  #define LOCAL near pascal

  void p_assem_line ( char       * line      ,
            char         label[MAXSYMLEN]   ,
            char         command[MAXSYMLEN],
            p_size_type   * size      ,
            char       * numterms      ,
            am_term_type ** termlist      ) ;


 unsigned long int    am_location_counter = 0L     ;
 char            am_end_found     = FALSE ;
 char            am_trunc_lines     = TRUE  ;

 /* These globals are used for relative symbol/term resolution by linker*/
 unsigned long int    am_relbase     = 0L     ;
 char            am_relknown     = FALSE ;


           /* size of absolute address in words. */
           /* 1 - abs short, 2 - abs long.   */
 char            am_abs_address_size  = 1;

 am_term_type       * am_term_list_head = NULL , * am_term_list_tail = NULL ;
 am_assem_type         am_assem_class   = am_absolute ;



/**************************************************************************
   Function am_resolve_symbol

     This function resolves a symbol list if it is possible
     If the symbol list is resolved it deletes every symbol
     node but the first one which contains the sum.
     It also compresses the symbol list as much as possible even
     if all symbol are not resolved.
       It compresses relative symbols by maintaining a count of
     relative symbols.   It adds or subtracts from this count
     according to the operator for the symbol.   If when the list
     is fully compressed, the relative count is not zero, then
   if the relative base is known, the final sum is
      computed by adding in relcount*am_relbase
   else
      relflag '*' is put into symbol[0]  and
      relcount is put into symbol[1] of the first symbol node.

     Globals
   am_relknown : flag indicating that relative symbol base value is
            known.
   am_relbase  : the known relative base.
     Variable parameters :
   symlist : the symbol list.

     Return value
   The number of symbols still left unresolved.
 *************************************************************************/
 int am_resolve_symbol( p_sym_type * symlist )
 {
   register int   symcount = 0 ;
   unsigned long  sum = 0L ;
   char      rel , relcount = 0 ;
   p_sym_type   * symbol , * temp , * prev ;

   temp = NULL ;
   prev = NULL ;
   symbol = symlist ;
   while ( symbol ) {

      rel = ( symbol->sym[0] == '*'  ) ;  /* set relative flag.*/

      if ( !rel && symbol->sym[0]  ) {       /* if there is a symbol.*/
   symcount++ ;
   prev = symbol ;
   symbol = symbol->next ;
   }
      else {           /* there is a value. perform calculations */
   if ( symbol->operator == '+' ) {
      if ( rel ) relcount += symbol->sym[1] ;
      sum += symbol->val ;
      }
   else {
      if ( rel ) relcount -= symbol->sym[1] ;
      sum -= symbol->val ;
      symbol->operator = '+';
      }

   if ( !temp ){        /* if temp == null */
      temp = symbol ;
      prev = symbol ;
      symbol = symbol->next ;
      }
   else {
      prev->next = symbol->next ;
      free(symbol);
      symbol = prev->next ;
      }
   }
      }

   /* if there were no unresolved symbols but relatives didn't cancel then*/
   if ( !symcount && relcount )
      if ( am_relknown ) {
    sum += ( relcount * am_relbase ) ;   /* resolve relative if known.*/
    relcount = 0;
    }
      else
    /* set symcount to 1 so that caller knows that chain is not resolved.*/
    symcount=1 ;

   if ( temp ) {
      temp->val = sum ;
      temp->operator = '+' ;
      if ( !relcount ) temp->sym[0] = 0 ;
      else {
    temp->sym[0] = '*' ;
    temp->sym[1] = relcount ;
    }
      }
   if ( prev )   prev->next = NULL ;

   return symcount ;

 } /* am_resolve_symbol */



/**************************************************************************
   Function am_resolve_term

     This function resolves a term list if it is possible.  It only
     resolves the terms until any the following conditions occur :
    - the first term class was am_first_instr_term and
      the next term is not am_other_instr_term
    - the terms classes are all am_data_terms and the
      datatermcount has been reached or am_first_instr_term
      has been reached.
    - the next term is NULL


     Variable parameters :
   termlist : The term list
   datatermcount : The number of data terms to resolve if the
         term class is am_data_term.
         This parameter is ignored if the first term
         was am_first_instr_term.
     Return value
   The number of terms still left unresolved.
 *************************************************************************/
 int am_resolve_term ( am_term_type * termlist , char   datatermcount )
 {
   register int    termcount = 0 ;
   am_term_type  * term ;
   char       okay , count ;

   term = termlist ;
   okay = TRUE ;
   count = 1 ;
   while ( term && okay ) {
      if ( am_resolve_symbol(term->symptr ) ) termcount++ ;
      term = term->next ;
      count++ ;
      if ( term )
    okay = ( termlist->class == am_first_instr_term &&
         term->class == am_other_instr_term ) ||
      ( termlist->class == am_data_term &&
        term->class == am_data_term  &&
        count       <= datatermcount   ) ;
      }

   return termcount ;

 } /* am_resolve_term */



/**************************************************************************
   Function am_delete_terms

      If the input parameter 'ALL' is set to TRUE (1) then
   this function frees all terms and associated symbols in a term list
   until null is encountered.
   The variable 'termlist' is set to NULL upon completion.
      If the input parameter 'ALL' is set to FALSE (0) then
   this function frees only one term and associated symbols.
   The variable 'termlist' is set to 'termlist->next' upon completion.

   Input
       all : boolean flag indicating how many terms to delete ( see above )
   Variable parameter
       termlist : a pointer variable which points to the termlist.


 **************************************************************************/
 void am_delete_terms ( am_term_type ** termlist , char   all )
 {
  am_term_type * term ;
  p_sym_type   * sym  ;

  term = *termlist ;
  if ( all ) {
     while ( term = *termlist ) {

       if (  ((term->modereg >> 8) == 7) &&
        ((term->modereg & 15)==10 ) ) {
     free(term->symptr);           /* free the string. */
     term->symptr = NULL ;
     }

       while ( sym = term->symptr ) {
     term->symptr = term->symptr->next ;
     free(sym) ;
     }
       *termlist = term->next ;
       free(term);
       }
    }
  else {
    if ( term ) {

      if (  ((term->modereg >> 8) == 7) &&
       ((term->modereg & 15)==10 ) ) {
    free(term->symptr);          /* free the string. */
    term->symptr = NULL ;
    }

      while ( sym = term->symptr ) {
    term->symptr = term->symptr->next ;
    free(sym) ;
    }
      *termlist = term->next ;
      free(term);
      }
    }

 } /* am_delete_terms */



/**************************************************************************
   Procedure am_add_terms_to_list

      This procedure links the list of terms into the global list
      of terms.
      This global list is used for all terms associated with data
      or instructions which contain forward/external references which
      are not yet resolved.

   Note : Term list is implemented as a non-circular doubly linked list.

   Globals :
     am_term_list_head : points to the head of the term list.
     am_term_list_tail : points to the tail of the term list.

   Variable parameter
       termlist : A pointer variable which points to the termlist to
        be linked in.  It is set to NULL when all the terms
        are transfered.


 **************************************************************************/
 void am_add_terms_to_list ( am_term_type ** termlist  )
 {
  am_term_type * term ;

  if ( ! (*termlist) ) return ;      /* if NULL then leave. */

  if ( !am_term_list_tail )  {        /* list is empty */
     am_term_list_head = *termlist ;
     am_term_list_head->prev = NULL ;
     }
  else {
     am_term_list_tail->next = *termlist ;
     am_term_list_tail->next->prev = am_term_list_tail ;
     }

  for ( term = *termlist ; term ; term = term->next )  /* set tail */
      am_term_list_tail = term ;

  *termlist = NULL;
  return ;       /* return used to avoid compilier warning. */

 } /* am_add_terms_to_list */



/**************************************************************************
   Procedure am_remove_terms_from_list

      This procedure removes the links from the global list of terms
      associated with the passed in termptr . It then deletes the terms
      by calling am_delete_terms.
      If the termptr points to a term with class am_first_instr_term
      then this routine will remove all terms for the instruction.
      If the termptr points to a term with class am_data_term
      then this routine will ONLY the ONE term.

   Note : Term list is implemented as a non-circular doubly linked list.

   Globals :
     am_term_list_head : points to the head of the term list.
     am_term_list_tail : points to the tail of the term list.

   Variable parameter
       termlist : A pointer variable which points to the termlist to
        be removed.  It is set to NULL when all the terms
        are deleted.

 **************************************************************************/
 void am_remove_terms_from_list ( am_term_type ** termlist  )
 {
  am_term_type * term ;
  char       i    ;

  if ( ! (*termlist) ) return ;      /* if NULL then leave. */

  term = *termlist ;
  i = 1 ;
  if ( term->class == am_first_instr_term ) {
     if ( term->next )
   if ( term->next->class == am_other_instr_term )
                /* remove both instr terms*/
      i = 2 ;
     }

  if ( term == am_term_list_head )
     am_term_list_head = ( i == 1 ) ? term->next : term->next->next ;
  else
     term->prev->next = ( i == 1 ) ? term->next : term->next->next ;

  if ( ( term == am_term_list_tail ) ||
       ( ( i == 2 ) && ( term->next == am_term_list_tail )) )
     am_term_list_tail = term->prev  ;
  else
    if ( i == 1 )
      term->next->prev = term->prev ;
    else
      if ( term->next->next ) term->next->next->prev = term->prev ;

  if ( i == 1 )
     term->next = NULL ;
  else
     term->next->next = NULL ;

  am_delete_terms( &term, TRUE );

  *termlist = NULL;
  return ;       /* return used to avoid compilier warning. */

 } /* am_remove_terms_from_list */


/*************************************************************************
    Procedure am_backfill

      This procedure updates the fields within the opcode.
      It determines actual post words for entire instruction, and
      places the opcode and post words into the listing.

      It expects all terms and symbols to be resolved.

      Input Parameter :
     termlist - will be resolved when finished creating final opcode.

  ************************************************************************/
  void   am_backfill(  am_term_type  * termlist )

  {
   char       i,j,k, reg    ;
   am_term_type *  term ;
   unsigned int    opcode ;
   l_line_type   *  lptr ;

   lptr = termlist->lineptr ;
   opcode = termlist->opcode;   /* get opcode */

   /* write opcode to listing */
   l_writetoline(1,opcode,0,lptr);

   term = termlist ;
   j =   ( term->index == 0 ) ? 2 : 1 ;
   reg = ( opcode & 7 ) ;

   for (i=1,k=2 ; i <= j && term ; i++ ) {
     switch ( reg ) {
       case 1 :     /* abs.l */
     l_writetoline(k,term->symptr->val>>16,0,lptr);
     k++ ;
       case 0 :     /* abs.w */
     l_writetoline(k,term->symptr->val & 0xFFFF ,0,lptr);
     k++ ;
     break ;
       }
      term = term->next ;
      reg = ((opcode>>9)&7) ;
     }

  } /* am_backfill */


/**************************************************************************
   Function am_readln

     This function reads in one source line from a file until the end-of-line
     or the end-of-file is encountered.

       - it only read up to 'linelen' number of charcters and discards
    the rest of the line.
       - expands TABS to 8 space charcters.
       - returns a blank flag indicating if the first 'linelen' characters
    are blank or not.
       - builds a separate string the same as the first but in uppercase
    in parameter line1.
     NOTE : characters within single or double qoutes are not affected.

     Typical calling method : ( for echo of exact file contents )
       while   ( readln(in_file,LINELEN-1,line,line1,&blank) != EOF )
         printf("%s\n",line);

     Input
    in_file  : the file to be read.
    linelen  : the max number of characters to be read not counting NULL.
     Variable
    line     : the line read in.
    line1     : the line read in converted to uppercase
    blank     : flag indicating whether or not the line is blank.

     Returns
       0 : line read okay.
       EOF : end of file was encountered.

 *************************************************************************/
static int LOCAL am_readln ( FILE * infile, int linelen,
              char * line  , char * line1, char * blank )
{
   int    ch, ch1 , i  ;
   char  dqoute, sqoute ; /* flags for tracking double & single quotes */

   /* if either flag is true then no conversion of case will take place.*/
   dqoute = sqoute = FALSE ;
   *blank = TRUE ;
   i = 0 ;
   while ( ch=ch1=fgetc(infile) ) {
      if ( ch == '\t' ) {                 /* expand tabs into 8 spaces */
    for ( ch = 0 ; ch < 8 ; ch ++, i++ ) {
       if ( i < linelen ) {
          line[i] = line1[i] = ' ';
          line[i+1] = line1[i+1]= NULL;
          }
       }
    continue ;
    }

      if ( !isprint(ch) ) break ;

      if ( ch == '\'' ) { if ( !dqoute ) sqoute = !sqoute ; }
      else if ( ch == '"' ) { if ( !sqoute ) dqoute = !dqoute ; }
      else if (!( dqoute || sqoute ))  ch1 = toupper(ch) ;

      if ( i< linelen ) {
     line[i]= ch ;
     line1[i]= ch1 ;
     if ( *blank ) *blank = ( ch == ' ' );
     line[i+1] = line1[i+1] = NULL ;
     }

      i++;
      }

   if ( i )   return 0 ;
   return ch ;

} /* am_readln */


/**************************************************************************
   Function am_pass1

     This function assembles an entire file and creates the listing
     and associted data structures as it does so.

     Input parameters :
       in_file  : the file containing the source assembly code
             which has already been opened for reading.


 *************************************************************************/

 void am_pass1 ( FILE * in_file , int  * error_count , int  * warning_count )

 {
  int         i  ;
  ps_pseudos      pseudo_class ;
  char         label[MAXSYMLEN], command[MAXSYMLEN] ;
  char         sizeinwords , blank ;
  p_size_type      size  ;
  p_sym_type    * sym    ;
  char         numterms ;
  am_term_type    * termlist, *term ;
  unsigned int      opcode ;
  unsigned int      index  ;
  char         prev_warnings = 0 ;
  char         line[LINELEN], linehold[LINELEN] ;
  l_line_type    * lptr  ;


  e_error.state = 0 ;
  e_error.warnings = 0 ;

  while ( !am_end_found && !e_error.out_of_memory &&
     ( am_readln(in_file,LINELEN-1,line,linehold,&blank) != EOF )) {

     if ( blank ){                  /* skip blank lines */
   l_addline( l_neither, 0, "", &lptr );
   continue ;
   }

     if ( line[0] == '*' || line[0] == ';' ) {   /* skip comment lines */
   l_addline( l_neither, 0, line, &lptr );
   continue ;
   }

     *error_count += e_error.state ;  /* count up total number of errors */
     e_error.state = 0 ;
     label[0]   = 0 ;
     command[0] = 0 ;
     size   = p_unknown ;
     numterms   = 0 ;
     sizeinwords= 0 ;
     termlist   = NULL ;

     p_assem_line ( linehold, label, command, &size, &numterms, &termlist );

     if ( e_error.state ) {
   /* add errors to listing and go on to next line */
   l_addline( l_neither, 0, line, &lptr);
   am_delete_terms(&termlist, TRUE );
   l_add_errors(lptr);
   continue ;     /* skip to next source line. */
   }

     /* if command is psuedo then handle it. */
     if ( ps_lookup_pseudo ( command, &pseudo_class ) ) {
   ps_pseudo( label, pseudo_class, numterms, &termlist, line);
   if ( ( e_error.warnings > prev_warnings ) || e_error.state )
      l_add_errors(l_line_head->prev);
   prev_warnings = e_error.warnings ;
   continue ;   /* skip to next source line. */
   }

     if ( am_location_counter & 1 ) {
   e_message(0,22,NULL);        /* location counter is odd */
   l_addline(l_neither, 0, line, &lptr);
   prev_warnings = e_error.warnings ;
   l_add_errors(lptr);
   am_location_counter++; /* adjust counter so this error does not repeat*/
   continue ;
   }

     /* its either an instruction or an error */

     if ( numterms > 2 ) {
   e_message(0,15,NULL) ;            /* too many terms on line.*/
   l_addline(l_neither, 0, line, &lptr);
   prev_warnings = e_error.warnings ;
   l_add_errors(lptr);
   continue ;
   }

     /* set size and initial opcode. */
     i = 1 ;
     switch ( numterms ) {
      case 0 :         /* make source and dest empty */
      i = is_validate ( &size , command, 7, 5, 7, 5,
         &opcode , &sizeinwords, termlist , &index );

      break;
      case 1 :         /* make source empty */
      i = is_validate ( &size , command, 7, 5,
         termlist->modereg >> 8,
         termlist->modereg & 15,
         &opcode , &sizeinwords, termlist , &index );

      break;
      case 2 :
      i = is_validate ( &size , command,
         termlist->modereg >> 8,
         termlist->modereg & 15,
         termlist->next->modereg >> 8,
         termlist->next->modereg & 15,
         &opcode , &sizeinwords, termlist , &index );

      break;
      }

     if ( i ) {
       /* error occured while validating. attach it to listing and
     go on to next line */
       l_addline(l_neither, 0, line, &lptr);
       prev_warnings = e_error.warnings ;
       l_add_errors(lptr);
       continue ;
       }

     /* do assembly if possible */

     /* process line label if there is one */
     if ( label[0] )
       if (sym_add_label_symbol(label,am_location_counter,am_assem_class)){
     l_addline(l_neither, 0, line, &lptr);
     l_add_errors(lptr);
     prev_warnings = e_error.warnings ;
     am_delete_terms(&termlist, TRUE) ;
     continue ;            /* skip to next source line. */
     }

     /* resolve already known symbols */
     for ( term = termlist ; term ; term = term->next )
       for ( sym = term->symptr ; sym ; sym = sym->next )
     if ( sym->sym[0] )
        sym_add_operand_symbol( sym, term, TRUE );


     l_addline( l_firstinstr, sizeinwords, line, &lptr   );

     if ( e_error.out_of_memory ) {
   am_delete_terms(&termlist,TRUE);
   break ;
   }

     /* its either an instruction or an error */
     if ( e_error.warnings > prev_warnings )
   l_add_errors(lptr);

     prev_warnings = e_error.warnings ;

     if (termlist) {
   termlist->lineptr = lptr ;      /* hold onto line.*/
   if ( termlist->next )
      termlist->next->lineptr = lptr ;
   termlist->index   = index     ;  /* hold onto index.*/
   termlist->opcode  = opcode    ;  /* hold onto opcode.*/
   }

     l_writetoline(0,am_location_counter,0,lptr);
     l_writetoline(sizeinwords,0,3,lptr);      /* put '?' in listing.*/

     /* are all terms knowm? */
     if ( ! am_resolve_term(termlist,0) ) {
   am_backfill( termlist );

   if ( e_error.warnings > prev_warnings )
      l_add_errors(lptr);
   prev_warnings = e_error.warnings ;

   am_location_counter += ( sizeinwords << 1 ) ;
   am_delete_terms(&termlist, TRUE) ;
   }
     else {
   am_add_terms_to_list( &termlist );
   am_location_counter += ( sizeinwords << 1 ) ;
   }

     }

  if ( e_error.out_of_memory ) {
   l_add_errors(l_line_head->prev);
   *error_count += e_error.state ;
   }

  /* Make sure all local and global symbols are resolved */
  e_error.state = 0 ;
  *error_count +=  sym_process_unresolved_locals() ;

  *warning_count = e_error.warnings ;

  /* Add symbol table to listing */
  sym_add_symtabtolisting();

 } /* am_pass1 */



<a name="01d8_0011"><a name="01d8_0011">
<a name="01d8_0012">
[LISTING THREE]
<a name="01d8_0012">

/*
  68000 error handler module

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "68defs.h"
#include "68err.h"


 struct e_struct e_error = { 0,0,0,0,FALSE, NULL, NULL };

 char e_hold_messages = TRUE ;
 e_printf_type e_printf ;

 /* Global error list array. */

 err_type   err_list[MAXERRORS] =
    {{ 1  , 0,"Error 1. Unexpected end of line." ,NULL,NULL },
     { 2  , 0,"Error 2. Unexpected symbol.",NULL,NULL },
     { 3  , 0,"Error 3. Unexpected token char.",NULL,NULL },
     { 4  , 0,"Error 4. Symbol/Literal contains invalid char.",NULL,NULL },
     { 10 , 0,"Error 10. Command op size invalid.",NULL,NULL },
     { 11 , 0,"Error 11. Invalid address mode.",NULL,NULL },
     { 12 , 0,"Error 12. Unrecognized command.",NULL,NULL },
     { 13 , 0,"Error 13. Command operands required.",NULL,NULL},
     { 14 , 0,"Error 14. Forward references not allowed here.",NULL,NULL},
     { 15 , 0,"Error 15. Too many operands.",NULL,NULL},
     { 16 , 0,"Error 16. Line label required.",NULL,NULL},
     { 17 , 0,"Error 17. Label found in external list.",NULL,NULL},
     { 18 , 0,"Error 18. Label already resolved.",NULL,NULL},
     { 19 , 0,"Error 19. Operand symbol already resolved.",NULL,NULL},
     { 21 , 0,"Error 21. Address collision.",NULL,NULL},
     { 22 , 0,"Error 22. Location counter is odd.",NULL,NULL},
     { 23 , 0,"Error 23. Unresolved symbol.",NULL,NULL},
     { 30 , 0,"Error 30. File not found or unable to open.",NULL,NULL },
     { 31 , 0,"Error 31. Unexpected end of file.",NULL,NULL },
     { 33 , 0,"Error 33. Disk full while writing file.",NULL,NULL},
     { 41 , 0,"Error 41. Out of Dynamic memory.",NULL,NULL },
     { 50 , 0,"Warning 50. Symbol too long. Truncated.",NULL,NULL},
     { 52 , 0,"Warning 52. Value out of range.",NULL,NULL},
     { 70 , 0,"Warning 70. Expression/Syntax imprecise.",NULL,NULL},
     { 71 , 0,"Warning 71. Command op size not specified.",NULL,NULL},
     { 72 , 0,"Warning 72. Line label not allowed. Ignored.",NULL,NULL},
     { 73 , 0,"Warning 73. Command operands not allowed. Ignored.",NULL,NULL},
     { 74 , 0,"Warning 74. Symbol already in global list. Ignored.",NULL,NULL},
     { 75 , 0,"Warning 75. Symbol already in external list. Ignored.",NULL,NULL},
     { 100, 0,"FSE 100. Invalid return from next_token.",NULL,NULL} };



/***************************************************************************
 Function e_message
      This function looks up the code in the errlist array, retrieves
      the standard message from the array, and inserts the message into
      the current errptr list .  It puts the additional message
      in the add_message field.

   Error code map :
     0..49  errors.
     50..99 warnings
     100..  fatal software errors.

 **************************************************************************/
 void  e_message( char    pos    ,
        char    code    ,
        char * add_mess)
 {
  err_type   * temp ;
  int          i    ;

  char * tmp;

  temp = NULL ;
  if ( e_hold_messages ) {
     temp = ( err_type * ) malloc ( sizeof(err_type) ) ;
     if ( !temp ) code = 41 ;  /* change code to that of 'out of memory' */
     }

  i = 0 ;
  while (( i < MAXERRORS ) && ( err_list[i].code != code )) i ++ ;

  if ( code == 41 )
     e_error.out_of_memory = TRUE ;

  if ( temp ) {
     temp->message = ( i <= MAXERRORS ) ? err_list[i].message : NULL ;

     if ( *add_mess )
   temp->add_mess = ( char * ) strdup ( add_mess ) ;
     else
   temp->add_mess = NULL ;

     temp->code    = code ;
     temp->position= pos ;
     temp->next = NULL ;

     if ( !e_error.errptr ) {
   e_error.errptr = temp ;
   e_error.last   = temp ;
   }
     else {
       e_error.last->next = temp ;
       e_error.last = temp ;
       }
     }

  tmp = ( char * ) ( i < MAXERRORS ) ? err_list[i].message : NULL ;

  if ( e_error.curpos )
    pos += e_error.curpos ;

  e_printf( tmp, add_mess );

  if ( ( code < 50 ) || ( code >= 100 ) ) {
     e_error.errors++ ;
     e_error.state++ ;
     }
  else
     e_error.warnings++;

 } /* e_message */



/***************************************************************************
 Procedure e_delete_errors
      This procedure deletes all the errors currently attached to
      e_error.errptr .

 **************************************************************************/
 void e_delete_errors()
 {
  err_type  * temp ;

  while ( e_error.errptr ) {
     temp = e_error.errptr->next ;
     if ( e_error.errptr->add_mess )
   free( e_error.errptr->add_mess ) ;
     free( e_error.errptr );
     e_error.errptr = temp ;
     }
 } /* e_delete_errors */



<a name="01d8_0013"><a name="01d8_0013">
<a name="01d8_0014">
[LISTING FOUR]
<a name="01d8_0014">


/*
     68000 Instruction Set Module.
    This module contains those procedures needed to handle
    the instruction set.
*/

  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>

  #include "68defs.h"
  #include "68err.h"
  #include "68parse.h"
  #include "68list.h"
  #include "68assem.h"
  #include "68instr.h"

  #define LOCAL near pascal


  /************************************************************************
    is_validate
      This procedure validates an instruction. It first looks up the
      instruction mnemonic in the is_array, then determines if the
      source and destination are valid for the particular instruction.

      Input parameters :
     size     : size of the operation. i.e   ADD.W
     command : the instruction mnemonic string.
      Variable parameters
     opcode  : the base operation code determined

     termlist : updates sizeofreserve field

     index   : the instruction array index for the instruction
      Return code :
     0       : instruction addr mode is valid.
     otherwise : error occured. Returned in error list.
   ************************************************************************/

  int   is_validate ( p_size_type *  size   ,
            char     *  command,
            char        smode  ,
            char        sreg   ,
            char        dmode  ,
            char        dreg   ,
            unsigned int * opcode ,
            char      * total_size ,
            am_term_type * termlist ,
            unsigned int * index    )

  {

    /* error if destination is not specified */
    if ( (dmode==7) && ( dreg==5 ) ) {
   e_message(0,11," Destination operand required.");
   return 11 ;
   }

    *total_size = 1 ;
    /* lookup command */
    if (  ! strcmp(command,"MOVE")  ) {
       /* error if source is not specified */
       if ( (smode==7) && ( sreg==5 ) ) {
     e_message(0,11," Source operand required.");
     return 11 ;
     }
       if ( *size == p_unknown ) {
     /* assign default size if it's unknown */
     e_message(0,71," Assumed Long.");
     *size = p_long ;
     }

       termlist->sizeofreserve=am_abs_address_size ;
       termlist->next->sizeofreserve=am_abs_address_size ;
       *total_size += ( am_abs_address_size << 1 );

       *opcode =  (dreg << 9 ) | ( dmode <<6 ) | ( smode <<3 ) | sreg ;
       *opcode |= ( *size == p_long )? 0x2000: (*size==p_word)? 0x3000:0x1000;
       *index = 0;
       }
    else if (  ! strcmp(command,"JMP")  ) {
       *opcode =  0x4EC0 | ( dmode << 3 ) | dreg ;
       termlist->sizeofreserve=am_abs_address_size ;
       *total_size += am_abs_address_size ;
       *index = 1 ;
       }
    else {
       e_message(0,12,command);
       return 12 ;
       }

    return 0 ;

  } /* is_validate */




<a name="01d8_0015"><a name="01d8_0015">
<a name="01d8_0016">
[LISTING FIVE]
<a name="01d8_0016">


/*
    68000 listing module.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

#include "68defs.h"
#include "68err.h"
#include "68list.h"


 /* These are considered module level variables. */
 l_line_type *   l_line_head = NULL ;
 char        *   l_header    =
  " Generic Assembler Version 1.0 :                            ";
 char        *   l_blanks    = "                        " ;
 int      l_number_of_lines = 0 ;

/****************************************************************************
  Function l_addline
     This funciton adds a line at the end of the line listing.
     It does this according to the class of the line :
   l_firstinstr : It creates a node for the text of the
             line to be held in.  It concats a blank
             leader before the text to hold the address
             and opcode. If there are more than two words
             in the instruction, then an additional
             line node in created.
   l_data        : It creates a node,and concats a blank leader onto
             the text line.

   Form of instruction line :
        blanks      line text
   addres |      |
first-> 000000    0000 0000 0000 Lable   command  operand text
   0--------------------23
other->     0000 0000
   0--------------------23
  NOTE :
     This routine copys the line text, so that the caller may reuse the
     line, or discard it with out affecting the listing.

  Input parameters :
     class   : the class of line to be added.
     numofwords : number of words in the instrcuction if its an
        instruction line.
     line   : the text of the line to be placed in the listing.

  Variable parameters :
     lineptr   : pointer to the line which was added to the listing.

  Return
     0   : line added okay.
     41 : out of memory

 **************************************************************************/
 int  l_addline( l_lclass_type     class      ,
       char        numofwords ,
       char      * line        ,
       l_line_type   ** lineptr    )
{
 l_line_type  * curline , * tcurline ;
 register int   i ;
 long      secs ;

 *lineptr = NULL ;
 curline = NULL ;
 curline = ( l_line_type * ) malloc ( sizeof(l_line_type) ) ;

 if ( !curline ) {
    e_message(0,41," Listing " ) ; /* out of memory */
    return 41 ;
    }

 curline->lclass = class ;
 curline->linenum = ( class != l_firstinstr ) ? 0 :l_number_of_lines++ ;

 if ( !l_line_head ) {
    l_line_head = ( l_line_type * ) malloc ( sizeof(l_line_type));
    if (!l_line_head){
      e_message(0,41 ," Listing " ) ; /* out of memory */
      return 41 ;
      }
    l_line_head->line = l_header ;
    time(&secs);
    strcpy ( ((l_line_head->line)+32), asctime( localtime(&secs) ) );
    i = strlen(l_line_head->line);
    for(;!isprint(l_line_head->line[i]);i--) l_line_head->line[i]=0;
    l_line_head->lclass = l_data ;
    l_line_head->linenum = 0 ;
    l_line_head->next = l_line_head ;
    l_line_head->prev = l_line_head ;
    }

 curline->line = ( char * ) malloc(  25 + strlen(line) );
 if ( !(curline->line) ) {
    e_message(0,41 ," Listing " ) ; /* out of memory */
    free(curline);
    return 41 ;
    }

 curline->next = l_line_head ;
 curline->prev = l_line_head->prev ;
 l_line_head->prev = curline ;
 curline->prev->next = curline;


 if ( class != l_neither ) {
    memset( (curline->line) ,'0', 6 );
    memset( ((curline->line)+6),' ',18 );
    }
 else
    memset( (curline->line) ,' ',24);

 strcpy( ((curline->line)+24) ,line);

 tcurline = curline ;

 if ( numofwords > 3 ) {
    /* allocate post lines for either instr or data */
    i = (numofwords - 3) / 3 ;
    if ( (numofwords - 3) % 3  ) i++ ;
    for ( ; i ; i-- ) {
       curline = ( l_line_type * ) malloc ( sizeof(l_line_type) ) ;
       if ( !curline ) {
     e_message(0,41 , NULL ) ; /* out of memory */
     return 41 ;
     }
       curline->next = l_line_head ;
       curline->prev = l_line_head->prev ;
       l_line_head->prev = curline ;
       curline->prev->next = curline;

       curline->line = strdup( l_blanks );
       if ( !(curline->line) ) {
     e_message(0,41 , NULL ) ; /* out of memory */
     return 41 ;
     }

       curline->lclass = ( class == l_firstinstr ) ? l_otherinstr:l_data ;
       curline->linenum =( class == l_firstinstr ) ? l_number_of_lines++ : 0 ;

       }

    }

 *lineptr = tcurline ;
 return 0 ;

} /* l_addline */



/****************************************************************************
  Function l_writetoline
     This funciton writes to a line in the listing, assuming it's
     already been created.
     It does this by refering to specific elements in the address opcode
     field by identifiers such as :
       0 : write 6 hex degit address field
       1 : write the 4 hex degit opcode/data field  for word 1 .
       2 : write the 4 hex degit opcode/data field  for word 2 .
       etc..
     It takes the data to be written ( whether address/data/opcode ) from
     the unsigned long parameter called :
   VALUE
     The option specifies special operations as follows :
       0 : write only one number as specified by spec .
       1 : write question marks in field for only one field specified by spec.
       2 : write repeated number over all post words up to the
      field specified by spec. ( spec 0 is ignored. )
       3 : write repeated question marks over all post words up to the
      field specified by spec. ( spec 0 is ignored. )
       4 : write only one byte at location specified by spec.

  Input parameters :
     spec   : the specification number as described above.
     value   : the value to be written on the line.
     option   : flag indicated if question marks are to be printed.
     line   : pointer to the listing node containing the first line.

 **************************************************************************/
 void   l_writetoline( int      spec      ,
             unsigned long   value      ,
             char      option      ,
             l_line_type    * line      )
{
 register int  i       ;
 char        * curline ;


 if ( !line ) return ;

 if ( !spec ) {
    if ( option )
       strnset(line->line,'?',6);
    else {
       sprintf(line->line,"%06X",value);
       *(line->line+6) = ' ' ;
       }
    }
 else {
    i = ( spec - 1 ) / 3 ;
    for (  ; i ; i-- , line = line->next ) {
   if ( option == 2 ) {
      sprintf(line->line+6,"   %04X %04X %04X",( int ) value,
         ( int ) value, ( int ) value );
      *(line->line+23) = ' ';
      }
   else if ( option == 3 )  {
      sprintf(line->line+6,"   ???? ???? ????" );
      *(line->line+23) = ' ';
      }
   }

    i = ( spec - 1 ) % 3  ;
    curline = line->line + 9 ;
    for ( ; i ; i-- , curline += 5 ) {
   if ( option == 2 )  {
      sprintf( curline,"%04X",( unsigned int ) value);
      *(curline+4) = ' ';
      }
   else if ( option == 3 )
      strnset( curline,'?',4 );
   }
    if ( ( option == 0 ) || ( option == 2 ) )  {
       sprintf(curline,"%04X", ( unsigned int ) value ) ;
       *(curline+4)=' ';
       }
    else if ( option == 4 ) {
       sprintf(curline,"%02X", ( unsigned char ) value );
       *(curline+2)=' ';
       }
    else
       strnset(curline,'?',4);
    }

} /* l_writetoline */


/****************************************************************************
  Function l_add_errors
     This funciton writes all the error messages attached to e_error.errptr
     to the listing, then deletes the error list.
  Input
     lptr  : pointer to the line in the listing after which the
        errors should be attached.

 **************************************************************************/
 void   l_add_errors ( l_line_type * lptr )
 {
  l_line_type   * tlptr ;
  err_type   * error ;
  char        relink_list ;
  char        message_buf[100] ;

  if ( !lptr )   /* if lptr == NULL */
      lptr = l_line_head->prev ;

  /* if lptr at end of list do not bother relinking listing.*/
  relink_list = ( lptr->next != l_line_head ) ;

  for ( error = e_error.errptr ; error ; error = error->next ) {
     strcpy( message_buf , error->message ) ;
     strcpy( message_buf+strlen(error->message), error->add_mess );
     l_addline(l_neither, 0, message_buf , &tlptr );
     if ( relink_list ) {
   /* unlink the  line */
   tlptr->next->prev = tlptr->prev ;
   tlptr->prev->next = tlptr->next ;

   /* link the line into listing after the source line at lptr */
   tlptr->next = lptr->next ;
   tlptr->next->prev = tlptr ;
   tlptr->prev = lptr ;
   lptr->next = tlptr ;
   }
     }
  e_delete_errors() ;

 } /* l_add_errors */


/****************************************************************************
  Function l_printlisting
     This funciton writes all the listing lines into the text file
     passed in as outfile. It assumes the file was already opened
     for text output. It will stop writing if an error occurs
     while writing.
  Input
     outfile : the already opened file that the listing is written to.
     withheader : TRUE if list header is to be written first.
        FALSE if list header not to be written at all.
  Return
     0 : okay.
     33: error while writing to file.

 **************************************************************************/
int  l_printlisting( FILE * outfile, char withheader )
{
 l_line_type * curline ;

 if ( withheader )
    fprintf(outfile,"%s\n",l_line_head->line);

 for ( curline = l_line_head->next ; curline != l_line_head ;
       curline = curline->next ) {
      if ( (curline->lclass != l_neither)&&(curline->lclass != l_data) ) {
   if ( fprintf(outfile,"%3d %s\n",curline->linenum,
           curline->line) == EOF ) {
      e_message(0,33,NULL);
      return 33 ;
      }
   }
      else if ( fprintf(outfile,"    %s\n", curline->line) == EOF ) {
    e_message(0,33,NULL);
    return 33 ;
    }
      }
 return 0 ;

} /* l_printlisting */


/****************************************************************************
  Function l_delete_listing
     This funciton deletes the entire listing, and resets all of
     its associated pointers back to their default values.
  Input
     deletehead : TRUE if head is to be deleted too.
        FALSE if head to be left alone.
     resetcount : TRUE if l_number_of_lines should be reset.


 **************************************************************************/
void l_delete_listing( char deletehead , char resetcount )
{
 l_line_type * curline, * tline ;

 if ( resetcount ) l_number_of_lines = 0 ;

 if ( l_line_head )  {
    l_line_head->prev->next = NULL ;
    curline = l_line_head->next ;

    l_line_head->next = l_line_head ;
    l_line_head->prev = l_line_head ;
    if ( deletehead ) {
       free(l_line_head);
       l_line_head = NULL ;
       }
    while ( curline ) {
     tline = curline->next ;
     if ( curline->line ) free( curline->line );
     free( curline );
     curline=tline ;
     }
    }

} /* l_delete_listing */



<a name="01d8_0017"><a name="01d8_0017">
<a name="01d8_0018">
[LISTING SIX]
<a name="01d8_0018">


/*
    68000 math module.
*/

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "68defs.h"
#include "68err.h"
#include "68parse.h"


/****************************************************************************
  Function strtolong
     This funciton converts a string of base to a long integer.

     It behaves the way that STRTOL is supposed to.

     Example
       num = strtolong("101", &endptr, 2);
       gives num = 5 ;

  Input parameters :
     str  : the string of valid numberic ascii characters
     endptr : if no error points to end of string .
         if error it points to error location in string.
     base : the base of the string : either 2, 8, 16, or 10
 **************************************************************************/
 unsigned long int strtolong ( register char   *  str     ,
                char      ** endptr ,
                char         base   )

{
   unsigned long int  sum  = 0L ;
   register char shift ;

   switch ( base ) {
     case 10 : for (; *str ; str++)
       if ( isdigit(*str) )
          sum = ( sum * 10 ) + ( *str - '0' ) ;
       else {
          *endptr = str ;
          return 0 ;
          }
          *endptr = str ;
          return sum ;
     case 2  : shift = 1 ; break ;
     case 8  : shift = 3 ; break ;
     case 16 : shift = 4 ; break ;
     }

   for (; *str ; str++ )
      if ( isdigit ( *str ) &&
     (  ( base == 10 ) || ( base == 16 ) ||
        ( (base == 2) && ( *str == '0' || *str == '1' ) ) ||
        ( (base == 8) && ( *str <= '7' && *str >= '0' ) ) ) )
    sum  = (  sum << shift  ) | ( *str - '0' ) ;
      else if ( isxdigit (*str) && ( base == 16 ) )
    sum  = ( sum << shift ) | ( toupper(*str) - 'A' + 10 ) ;
      else {
    *endptr = str ;
    return 0 ;
    }

   *endptr = str ;
   return sum ;

} /* strtolong */



/***************************************************************************
 Function m_symtoval

    This function converts a valid numerical symbol string to its
    corresponding long value. It follows the format below :
   All symbols which start with an 0..9,%,@,$," are numerical symbols.
   All others are ordinary symbols.

   Numeric
       $ddd  -Hex    %ddd - Binary     @ddd - Octal    ddd - Decimal
        daaH     dddB         dddO     dddD
                  dddQ

       '4444' - Quoted literal of max 4 Ascii characters.


     0   : a valid number is returned
     4   : an invalid char was found in symbol
     otherwise its not a valid number, although it may be a valid symbol.

 ***************************************************************************/
int    m_symtoval( char * sym , unsigned long int  * value )

{
  char     symbol[MAXSYMLEN] ;
  char     ch , * last , base ;


  strncpy( symbol, sym, MAXSYMLEN );  /* make a local pass by value symbol*/
  symbol[MAXSYMLEN-1] = 0 ;

  last = symbol+strlen(symbol)-1 ;
  *value = 0L ;

  if ( isdigit( ch=*symbol ) )
     switch ( toupper( *last )   ) {
       case 'H' : *last = 0 ; base = 16 ; break ;
       case 'B' : *last = 0 ; base = 2  ; break ;
       case 'Q' :
       case 'O' : *last = 0 ; base = 8  ; break ;
       case 'D' : *last = 0 ;
       default   :  base = 10 ; break ;
       }
  else {
     switch ( ch ) {
   case '$' : symbol[0] = '0' ; base = 16 ; break ;
   case '@' : symbol[0] = '0' ; base = 8 ; break ;
   case '%' : symbol[0] = '0' ; base = 2 ; break ;
   case '\'':
         /* scan line until next ' */
         last = strchr( symbol+1, '\'');
         if ( !(*last) ) {
            e_message(0,51,"End quote expected.");
            last = symbol+5 ;
            }
         *last = 0 ;
         if ( last - symbol  > 5 ) {
             e_message(0,51,NULL);
             symbol[5] = 0 ;
             }
         for ( last = symbol+1 ; *last ; last++ )
             *value = ( *value << 8 ) | *last ;
         return 0 ;
   default  : return 1 ;
   }
     }

     *value = ( unsigned long int ) strtolong( symbol, &last , base );
     if ( *last ) e_message(0,4,NULL);
     return  ( *last  ) ? 4 : 0 ;

} /* m_symtoval */





<a name="01d8_0019"><a name="01d8_0019">
<a name="01d8_001a">
[LISTING SEVEN]
<a name="01d8_001a">


/*
    68000 parser module.
*/


#include <stdio.h>
#include <alloc.h>
#include <string.h>
#include <ctype.h>

#include "68defs.h"
#include "68err.h"
#include "68parse.h"
#include "68list.h"
#include "68assem.h"
#include "68math.h"

#define LOCAL near pascal

   typedef enum { symbol, token, none } p_tok_type ;

   typedef enum {  pn_sym   ,        /* sym */
         pn_empty ,        /* empty parse node. */
         pn_error        /* error */
      } p_addr_type ;

   typedef struct {
         p_addr_type    p_nodeclass;/* tag field */
         char     regnum   ;
         p_sym_type *  symptr  ; /* symbol list, if any. */
        } p_node_type ;



    char     symchars[38] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_" ;

    p_sym_type     curtoksym ;      /* current token symbol from next_token */
    char     curtok ;      /* current token character from next_token*/


 /* Function prototypes for local functions */

 static p_tok_type LOCAL next_token( char * line     , int  * line_offset  ,
                 int    line_len );

 static int LOCAL  p_symbol ( char *     line      , int  * line_offset   ,
               int     line_len , char   symknown     ,
               char     preop    , p_sym_type ** symptr ,
               p_node_type * p_node   );

 static p_size_type LOCAL p_dotsize ( char * line    ,
                  int  * line_offset ,
                  int    line_len ,
                  int    prev_offset );
 static void LOCAL p_parse_line ( char *   line     , int  * line_offset    ,
              int      line_len , p_node_type *  p_node ,
              char *   done     );




/************************************************************************

 Function next_token  - get next token

    This function returns the next token in the text line from
    the current line offset.  If rest of line is blank, or if
    line offset is at end of line, then it returns an end of line
    indicator.

    Input parameters :
       line       - the line to be parsed.
       line_offset  - the current offset into the line.
       line_len     - the line length. ( so as to avoid calling strlen )

    Calls :
      p_symtype : to reclassify symbols .

    Warnings : e_message called as warnings occur.
      50 : Symbol too long

    Globals :
      curtoksym
      curtok

    Return value :
      symbol : symbol found.  Is located in curtoksym.
      token  : token found.   Token character is in curtok.
      none   : end of line found.

 *************************************************************************/
  static p_tok_type LOCAL next_token( char * line     , int  * line_offset  ,
                  int    line_len )

{
  char          * tokpos, * tline ;
  int          symlen  ;


  (*line_offset) += strspn( line + *line_offset , " \t");  /* skip blanks */

  if ( *line_offset >= line_len )
     return none ;

  tline = line + *line_offset ;

  if ( *tline == '\'') {
     tokpos = strchr( tline+1, '\'');      /* scan for next quote.*/
     if ( *tokpos )            /* if found set tok one further. */
   tokpos++ ;            /* if not , set to shortest of   */
     else               /* (5 forward or the end)       */
   tokpos = ( tokpos - tline > 5 ) ? tline + 5 : tokpos ;
     }
  else
    tokpos = strpbrk( tline, ".;-+()#/, \"\t");


  if ( tokpos != tline ) {
     /* return symbol between start of line and tokpos. */
     if ( !(*tokpos) ) tokpos = line + line_len ;  /* correct for NULL */
     symlen = tokpos - tline ;
     (*line_offset) += symlen;
     if ( symlen >= MAXSYMLEN ) e_message(*line_offset,50,NULL);
     symlen = ( symlen >= MAXSYMLEN ) ? MAXSYMLEN - 1 : symlen ;
     strncpy( curtoksym.sym, tline, symlen ) ;
     curtoksym.sym[symlen] = 0 ;

     if ( isalpha(curtoksym.sym[0] ) )
   if ( strspn( curtoksym.sym, symchars ) == symlen )
      return symbol ;
   else {
      e_message(*line_offset,4,NULL);
      return symbol ;
      }

     e_error.curpos = *line_offset ;
     if ( ( !m_symtoval(curtoksym.sym,&curtoksym.val ) ) &&
     ( !e_error.state ) )
     curtoksym.sym[0] = 0 ;
     e_error.curpos = 0 ;

     return  symbol ;
     }
  else
     {
     /*  the token is at the start of the line. */
     curtok = *tline ;
     (* line_offset ) ++ ;
     return token ;
     }

} /* next_token */



/***************************************************************************

 Function p_symbol  - parse symbol

    This function parses a symbol into its components following the
    diagram :

       symbol --> operator --->
     ^          |
     |-----------------|

       symbol    - any valid non-key word of SYMMAXLEN length or less.
       operator  -  plus  '+'
          minus '-'

       If an unrecognized operator is encountered, the symbol chain
     is assumed to have come to an end.  The last token is then
     un-read, so that it can be re-read by subsequent parse code.

    Input parameters :
       line       - the line to be parsed.
       line_offset  - the current offset into the line.
       line_len     - the line length. ( so as to avoid calling strlen )
       symknown     - flag ( TRUE or FALSE ) which indicates whether
            the initial symbol was already parsed.

    Note : calls e_message to print errors as it recurses up.

    Warnings : e_message called as warnings occur.
      51 : literal too long

    Return code :
      0 : okay
      1 : Unexpected eoln
      2 : Unexpected symbol.
      3 : Unexpected token char.
      41  : Out of dynamic memory.
      100 : Invalid return from next token.
 **************************************************************************/
 static int LOCAL  p_symbol ( char *     line      , int  * line_offset   ,
               int     line_len , char   symknown     ,
               char     preop    , p_sym_type ** symptr ,
               p_node_type * p_node   )
{
  p_tok_type   tok ;
  int          i ;


  if ( symknown )
     tok = symbol ;
  else
     tok = next_token( line , line_offset , line_len) ;

  switch ( tok ) {
     case symbol : /* Symbol was found */
         *symptr = ( p_sym_type * ) malloc ( sizeof(p_sym_type));
         if ( ! (*symptr) ) {   /* if *symptr == NULL */
            e_message(0,41," Parser ");
            return 41 ;
            }

         memcpy( (*symptr) , &curtoksym, sizeof(p_sym_type) );
         (*symptr)->operator = preop ;
         (*symptr)->next = NULL ;

         i = *line_offset ; /* hold onto line offset */

         tok = next_token( line , line_offset , line_len);
         switch ( tok ) {
           case token :
           switch ( curtok ) {
              case '+' :
              case '-' :
             i = p_symbol( line, line_offset,
                  line_len, FALSE, curtok,
                  &( (*symptr)->next ), p_node);
             return i ;
              default  :
             /* un-get the token. */
             *line_offset = i ;
             return 0;
             }
           case symbol : e_message(*line_offset,2,NULL);
               return 2 ;
           case none    : return 0 ;
           default    : e_message(*line_offset,100,"p_symbol");
               return 100 ;
           }
     case token  : e_message(*line_offset,3,NULL);
         return 3 ;
     case none    : e_message(*line_offset,1,NULL);
         return 1 ;
     default    : e_message(*line_offset,100,"p_symbol");
         return 100 ;
     }

} /* p_symbol */



/**************************************************************************
 Function p_dotsize
    This function parses a dot size field and returns the size as a type.
    It expects that the current token is a token/delemeter and not a symbol

    If the curtok is not a '.' it unreads it and returns p_unknown.
    If the curtok is a '.' it follows :
   .B  -- returns p_byte
   .W  -- returns p_word
   .L  -- returns p_long
   . anything else -- returns p_unknown, and unreads the last token
            excluding the period.  It also prints out the
            warning that the period was ignored.

 **************************************************************************/
 static p_size_type LOCAL p_dotsize ( char * line    ,
                  int  * line_offset ,
                  int    line_len ,
                  int    prev_offset )
{
  p_tok_type  tok     ;

  if ( curtok == '.' ) {
    prev_offset = *line_offset ;
    tok = next_token( line , line_offset, line_len);
    if ( (tok == symbol )&&
    ( curtoksym.sym[1] == NULL ) )
       switch ( curtoksym.sym[0] ) {
     case 'B': return p_byte ;
     case 'W': return p_word ;
     case 'L': return p_long ;
     }
    e_message(prev_offset,70," '.' ignored.");
    *line_offset = prev_offset ;
    return p_unknown ;
    }
  else {
    *line_offset = prev_offset ;
    return p_unknown ;
    }
} /* p_dotsize */



/***************************************************************************

 Function parse_line - parse line

    This function parses a line into its components following the
    diagram :

     <---------------------------feedback------------|
    |                       |
    |  symbol ---> p_symbol  ------------------>','--|
                 |---->';'---->
                 |---->eoln--->
                 |---->error-->

       symbol          - any valid key word of SYMMAXLEN length or less.
       string literal  - quoted string. "example of string."
       eoln          - end of line reached
       error          - error in parsing

    Input parameters :
       line       - the line to be parsed.

 **************************************************************************/
 static void LOCAL p_parse_line ( char *   line     , int  * line_offset    ,
              int      line_len , p_node_type *  p_node ,
              char *   done     )
{
  p_tok_type   tok ;

  tok = next_token( line , line_offset , line_len) ;
  p_node->p_nodeclass = pn_empty ;
  p_node->symptr = NULL ;

  switch ( tok ) {
     case symbol :
         p_node->p_nodeclass = pn_sym ;
         p_symbol(line, line_offset, line_len , TRUE,
             '+',&(p_node->symptr),p_node );
         break;
     case token  : switch ( curtok ) {
            case ';' : *done = TRUE ;  /* Comment found */
             break;
            default  : e_message(*line_offset,3,NULL);
             *done = TRUE ;
             break;
            }
         break ;
     case none    : *done = TRUE ;   /* End of line found */
         break ;
     default    : e_message(*line_offset,100,NULL);
     }

  *done = ( e_error.state ) ? TRUE:*done ;

  if ( ! *done ) {
  tok = next_token( line , line_offset , line_len) ;
  switch ( tok ) {
     case symbol :
      e_message(*line_offset,2,curtoksym.sym); /* Unexpected symbol */
      *done = TRUE ;
      break ;
     case token  :
      switch ( curtok ) {
         case ',' : break;
         case ';' : *done = TRUE ;              /* Comment found */
          break;
         default  : e_message(*line_offset,3," ',' or ; exp.");
          *done = TRUE ;
          break;
         }
      break ;
     case none    : *done = TRUE ;   /* End of line found */
         break ;
     default    : e_message(*line_offset,100,NULL);
         *done = TRUE ;
     }
  }

  if ( e_error.state ) p_node->p_nodeclass = pn_error ;

} /* p_parse_line */



/***************************************************************************
   Function p_assem_line

     This function returns parses one line into a list of terms.

     Input parameters :
   line  : the line of source text to be parsed.

     Variable parameters
   label    : the label found for the line.
   command  : the command found on the line.
   size    : the size specification of the command. ie. MOVE.B is p_byte.
   numterms : the number of terms parsed.
   termlist : the term list in order of parsing

 **************************************************************************/
 void p_assem_line ( char      * line          ,
           char        label[MAXSYMLEN]  ,
           char        command[MAXSYMLEN],
           p_size_type   * size          ,
           char      * numterms          ,
           am_term_type ** termlist          )

 {
  register int    i   ;
  int       line_offset = 0, line_len;
  char       done   ,  mode,  reg ;
  am_term_type * term , * prev ;
  p_tok_type    tok   ;
  p_node_type    p_node ;        /* current parse node. */

  e_error.state = 0 ;

  /* clear the parameters */
  label[0] = 0 ;
  command[0] = 0 ;
  *size = p_unknown ;
  *numterms = 0 ;
  *termlist = NULL ;

  line_len = strlen(line) ;

  /* read the line label */
  if ( isalpha( line[0] ) ) {
      /* scan the line to remove the label string. */
      tok = next_token( line , &line_offset , line_len) ;
      if (tok == symbol)
    strncpy( label,curtoksym.sym,MAXSYMLEN );
      }

  /* read the command */
  i = line_offset ;
  tok = next_token( line , &line_offset , line_len) ;
  switch ( tok ) {
     case symbol : strncpy(command,curtoksym.sym,MAXSYMLEN);
         /* check for size identifier */
         i = line_offset ;
         tok = next_token( line , &line_offset , line_len) ;
         switch ( tok ) {
            case symbol : line_offset = i ; break ;
            case token  : *size = p_dotsize(line,&line_offset,
                        line_len,i);
                break;
            case none   : line_offset = i ; break ;
            }
         break ;
     case token  :
     case none    : line_offset = i; break ;
     }

  i = 0 ;
  done = FALSE ;
  while ( ! done ) {
     p_parse_line( line, &line_offset,line_len,&p_node,&done);
     if ( e_error.state ) break ;
     if ( p_node.p_nodeclass == pn_empty ) break ;

     switch ( p_node.p_nodeclass ) {
   case  pn_sym   : mode = 7 ;
          reg = ( am_abs_address_size == 1 )?0:1 ; break ; /* sym   */
   case  pn_empty : mode = 7 ; reg = 5    ; break ; /* empty/none*/
   case  pn_error : mode = 7 ; reg = 11    ; break ; /* error  */
   }

     term = ( am_term_type * ) malloc ( sizeof( am_term_type ) ) ;
     if ( !term ) {
    e_message(0,41,"Parser");   /* not enough memory */
    break ;
    }

     term->modereg = ( mode << 8 ) | reg ;
     term->symptr = p_node.symptr ;
     term->next = NULL ;
     term->prev = NULL ;
     term->sizeofreserve = 0 ;
     term->postword = 0 ;
     term->class = am_other_instr_term ;

     (*numterms)++ ;
     if ( !(*termlist) )  /* if termlist = NULL */
       *termlist = term  ;
     else {
       term->prev = prev ;
       prev->next = term ;
       }
     prev = term ;

     } /* while */

  if ( *termlist )
     (*termlist)->class = am_first_instr_term ;

 } /* p_assem_line */




<a name="01d8_001b"><a name="01d8_001b">
<a name="01d8_001c">
[LISTING EIGHT]
<a name="01d8_001c">


  /*
     68000 Pseudo Module.
    This module contains those procedures needed to handle
    psuedo command assembly.
  */

  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>

  #include "68defs.h"
  #include "68err.h"
  #include "68parse.h"
  #include "68list.h"
  #include "68assem.h"
  #include "68symtab.h"
  #include "68pseudo.h"


  typedef struct  {  char      pseudo[10];
           ps_pseudos    index  ;
        } ps_pseudo_type ;

  ps_pseudo_type  ps_pseudo_array [7] =
             { { "ABS_LONG"  , ps_abslong } ,
               { "ABS_SHORT" , ps_absshort} ,
               { "END"       , ps_end     } ,
               { "EQU"       , ps_equate  } ,
               { "EXTERN"    , ps_extern  } ,
               { "GLB"       , ps_global  } ,
               { "ORG"       , ps_origin  } } ;



/***************************************************************************
   Function ps_lookup_pseudo

     This function looks up a pseudo command in the pseudo array and
     returns the psuedo class type if it is a valid pseudo command.

     Input parameter :
   pseudo   : the pseudo command being looked up.
     Variable parameter :
   pseudo_class : the class which is returned.

     Return code
   0 : pseudo not found.
   1 : pseudo found.

 **************************************************************************/
 int ps_lookup_pseudo ( char      * pseudo     ,
         ps_pseudos * pseudo_class )
 {
  ps_pseudo_type * indx ;

  if ( indx = ( ps_pseudo_type * ) bsearch( pseudo, ps_pseudo_array, 7,
                 sizeof(ps_pseudo_type),strcmp) ){
     *pseudo_class = indx->index ;
     return 1 ;
     }
  else
     return 0 ;

 } /* ps_lookup_pseudo */


/***************************************************************************
   Function ps_one_symbol_only

     This function returns true if the symbol list contains only one
     symbol, and no literals.  Otherwise, it returns false.

     For an empty list it returns false.

     Used by am_pseudo for EXTERNAL and GLOBAL operand validation.

     Input parameter :
   symlist : the symbol list

 **************************************************************************/
 int ps_one_symbol_only ( p_sym_type * symlist )
 {
   if ( symlist )
      if ( symlist->next )
    return FALSE ;
      else
    if ( symlist->sym[0] )
       return TRUE ;
    else
       return FALSE ;
   else
     return FALSE ;
 } /* ps_one_symbol_only */



/***************************************************************************
   Function ps_validate_pseudo

     This function validates the pseudo commands depending on the
     parameters passed in as listed below :
    Note : each action is based on a TRUE value for the variable.
    locationeven    : If location counter not even then error.
    onetermonly    : If number of terms not equal to one then an
            error results.
    zeroterms    : If number of terms >= 1 then error.
    labelrequired    : If there is no label then error.
    ignorlable    : If there is a label then warning.
    forwardsallowed : If symbols are forward referenced then no error
            else error
    stringsallowed  : If a term is found which is a string then
             no error is issued.
    onesymbolonly    : Each term can only have one unresolved symbol
            in it or an error will result.

  Input parameters :
    label     : pointer to label found on current line.
    numterms : the number of terms in the termlist.

  Variable parameter
    termlist : Pointer to the term list.  The termlist is
          deleted in the event of an error.

  Note : On any error, the termlist is completely deleted, and
    the appropriate error message is in the error list created
    by e_message.

  Return code
     0   : validated.
     other : error in validation.

 **************************************************************************/
 int  ps_validate_pseudo ( char     * label    ,
            char       numterms ,
            am_term_type ** termlist ,
            char       onetermonly     ,
            char       zeroterms     ,
            char       forwardsallowed,
            char       labelrequired  ,
            char       ignorlabel     ,
            char       stringsallowed ,
            char       onesymbolonly  )

 {
  am_term_type * term ;
  p_sym_type   * sym  ;
  int       i   , mode, reg  ;



  if ( ( onetermonly && ( numterms > 1 )) ||
       ( zeroterms   && ( numterms     )) ) {
     e_message(0,15,NULL) ;          /* requires one operand */
     i = 15 ;
     goto deleteterms ;
     }

  if ( onetermonly && ( numterms == 0 ) ) {
     e_message(0,13,NULL) ;          /* requires at least one operand */
     return 13 ;
     }

  if ( labelrequired && !(*label) ) {
     e_message(0,16,NULL) ;         /* label required */
     i = 16 ;
     goto deleteterms ;
     }

  if ( ignorlabel && *label )
     e_message(0,72,NULL ) ;          /* label ignored. */

  if ( numterms )
     /* validate each term */
     for  ( term = *termlist ; term ; term= term->next ) {
   mode = term->modereg >> 8 ;
   reg  = term->modereg &  15 ;
   if (  ( mode == 7 ) && ( reg <= 1 ) ) {
      if ( onesymbolonly ) {
         if ( ! ps_one_symbol_only(term->symptr)  ) {
       e_message(0,11,NULL)  ; /* illegal term */
       i = 11 ;
       goto deleteterms ;
       }
         sym_add_operand_symbol( term->symptr, term, FALSE );
         if ( ( term->symptr->sym[0] == '*' ) ||
         ( !term->symptr->sym[0] )) {
         e_message(0,19,NULL);
         i = 19 ;
         goto deleteterms ;
         }
         }
      else {
         for ( sym = term->symptr ; sym ; sym = sym->next ) {
       if ( sym->sym[0] )
          sym_add_operand_symbol( sym, term, FALSE );

       }
         /* compress symbol chain and hold onto number of unresolved */
         /* symbols in postword                 */
         term->postword = am_resolve_symbol(term->symptr) ;

         if ( ( ! forwardsallowed ) && ( term->postword ) ) {
       e_message(0,14,NULL) ;    /* cannot forward reference */
       i = 14 ;
       goto deleteterms ;
       }
         }
      }
   else {
      if ( ! ( stringsallowed && ( mode == 7 ) && ( reg == 10 ) )) {
        e_message(0,11,NULL)  ; /* illegal term */
        i = 11 ;
        goto deleteterms ;
        }
      }
   }


  return 0 ;


  deleteterms :        /* label for exit with deletion of terms */
             /* i will contain the error code returned*/

  if ( numterms )            /* delete all terms */
    am_delete_terms( termlist, TRUE );

  return i ;


 } /* ps_validate_pseudo */




/***************************************************************************
  Function ps_pseudo

     This function handles all the pseudo commands for the assembler.

  Input parameters :
    label     : pointer to label found on current line.
    index     : the pseudo index
    size     : the size specification of the pseudo command. ( ie .B )
    numterms : the number of terms in the termlist.

  Variable parameter
    termlist : Pointer to the term list.  The termlist is
          deleted except for those terms which are not
          resolved for the particular pseudos which allow
          forward references.
  Globals :
   am_location_counter : updated when pseudo requires it.
   am_end_found       : set to TRUE if 'end' pseudo is encountered.
   am_abs_address_size : set to 1 for abs_short, 2 for abs_long.

  Note : On any error, the termlist is completely deleted, and
    the appropriate error message is in the error list created
    by e_message.

  Return code
     0   : pseudo handled okay.
     other : error occured.

 **************************************************************************/
 int  ps_pseudo ( char      * label    ,
        ps_pseudos     index    ,
        char        numterms ,
        am_term_type ** termlist ,
        char      * line      )
 {
  am_term_type * term  ;
  int       i     ;
  l_line_type  * lptr  ;

  switch ( index ) {
     case ps_abslong : case ps_absshort :
     case ps_even    : case ps_end   :
     l_addline( l_neither , 0, line, &lptr); /* add line to listing */
     if ( *label ) e_message(0,72,NULL)   ; /* label ignored. */
     if ( numterms ) e_message(0,73,NULL)   ; /* operands ignored. */
     switch ( index ) {
        case ps_abslong  : am_abs_address_size = 2 ; break ;
        case ps_absshort : am_abs_address_size = 1 ; break ;
        case ps_even     : if ( am_location_counter & 1 )
               am_location_counter++ ;
            break;
        case ps_end      : am_end_found = TRUE ; break ;
        }
     am_delete_terms( termlist, TRUE ) ;
     return 0 ;

     case ps_equate :
     l_addline(l_neither,0,line,&lptr) ;
     /* requires one term only. label required. */
     if ( i = ps_validate_pseudo ( label, numterms, termlist,
            TRUE,FALSE,FALSE,TRUE,FALSE,FALSE,FALSE))
        return i ;

     /* add label and value to symbol table */
     i = sym_add_label_symbol( label, (*termlist)->symptr->val,
                am_absolute ) ;
     am_delete_terms( termlist, TRUE ) ;
     return i ;

     case ps_extern :
     l_addline(l_neither,0,line,&lptr) ;
     /* one or more terms. label ignored. one symbol per each term */
     if ( i = ps_validate_pseudo ( label, numterms, termlist,
            FALSE,FALSE,FALSE,FALSE,TRUE,FALSE,TRUE))
        return i ;

     /* Add each symbol to the external symbol list. */
     for ( term = *termlist ; term ; term = term->next )
         if ( i = sym_add_extern(term->symptr->sym) ) {
       am_delete_terms( termlist, TRUE );
       return i ;
       }

     am_delete_terms( termlist, TRUE );
     return 0 ;

     case ps_global :
     l_addline(l_neither,0,line,&lptr) ;
     /* one or more terms. label ignored. one symbol per each term */
     if ( i = ps_validate_pseudo ( label, numterms, termlist,
             FALSE,FALSE,FALSE,FALSE,TRUE,FALSE,TRUE))
        return i ;

     /* Add each symbol to the global symbol list. */
     for ( term = *termlist ; term ; term = term->next )
         if ( i = sym_add_global(term->symptr->sym) ) {
       am_delete_terms( termlist, TRUE );
       return i ;
       }

     am_delete_terms( termlist, TRUE );
     return 0 ;

     case ps_origin :
     l_addline(l_neither,0,line,&lptr) ;
     /* requires one term only. label ignored. */
     if ( i = ps_validate_pseudo ( label, numterms, termlist,
                TRUE,FALSE,FALSE,FALSE,TRUE,FALSE,FALSE))
        return i ;
     am_location_counter = (*termlist)->symptr->val ;
     am_delete_terms( termlist, TRUE );
     return 0 ;
     }

  return 0 ;

 } /* ps_pseudo */



<a name="01d8_001d"><a name="01d8_001d">
<a name="01d8_001e">
[LISTING NINE]
<a name="01d8_001e">


  /*
     68000 Symbol table Module.
    This module contains those procedures needed to handle
    the symbol table.
  */

  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>

  #include "68defs.h"
  #include "68err.h"
  #include "68parse.h"
  #include "68list.h"
  #include "68assem.h"
  #include "68symtab.h"


  /* Module level variable */
  sym_label_type    *  sym_glb_lab_head = NULL ;
  sym_label_type    *  sym_local_lab_head = NULL ;

  sym_operand_type  *  sym_ext_ref_head = NULL ;
  sym_operand_type  *  sym_local_ref_head = NULL ;


  /* Local prototypes */

  sym_operand_type * sym_lookup_extern( char  *  symbol );
  sym_label_type   * sym_lookup_local ( char  *  symbol );
  int           sym_addtolocaloplist ( p_sym_type     * symptr  ,
                   am_term_type  * termptr ) ;


/***************************************************************************
  Function sym_add_symtabtolisting

     This function is meant to be called at the end of assembly.
     It adds the symbol tables to the listing if there are any
     global, external, or local symbols .

     The symbol table is of the following form :

       Symbol        Value    Class
       ----------    ------    ------
       MYSYMBOL      000000    Global  Relative
       MYSYMBOL1     00FFFF    Global  Absolute
       MYSYMBOL2     ??????    Global  Unknown
       MYSYMBOL3     001ABC    Local    Relative
       MYSYMBOL4     ??????    Extern  Unknown

  Calls
     l_addline : to add the text lines to the listing.

 **************************************************************************/
  void sym_add_symtabtolisting()
  {
    register  sym_label_type   * labptr ;
    sym_operand_type * refptr ;
    l_line_type      * lptr  ;
    char        * message_buf , * chptr , count ;
    int           i ;


    if ( sym_glb_lab_head || sym_local_lab_head || sym_ext_ref_head ) {
       l_addline(l_neither, 0,"",&lptr);
       l_addline(l_neither, 0,
        " Symbol                Value     Class ",&lptr);
       l_addline(l_neither, 0,
        " ----------------      ------    -------",&lptr);
       }
    else
       return ;

    message_buf = "                                                      " ;

    for (count = 1 ; count <= 2 ; count++ ) {
       if ( count == 1 ) {
     strncpy(message_buf+33,"Global ",7);
     labptr = sym_glb_lab_head ;
     }
       else {
     strncpy(message_buf+33,"Local  ",7);
     labptr = sym_local_lab_head ;
     }

       for ( ; labptr ; labptr= labptr->next ) {

     i = strlen( labptr->symbol );
     strncpy( message_buf + 1, labptr->symbol, i );
     for ( chptr = message_buf + i + 1 ; i < MAXSYMLEN ; i++, chptr++ )
          *chptr = ' ' ;

     if ( labptr->relative == '?' ) {
        strncpy( message_buf + 23 ,"??????",6 );
        strncpy( message_buf + 43," Unknown  ", 10 );
        }
     else {
        sprintf( message_buf+ 23 ,"%06lX",labptr->val );
        message_buf[29] = ' ' ;
        if ( labptr->relative == '*' )
      strncpy( message_buf + 43," Relative ", 10 );
        else
      strncpy( message_buf + 43 ," Absolute ", 10 );
        }
     l_addline(l_neither, 0, message_buf , &lptr );

     }
       }

    strncpy( message_buf + 23, "??????",6 );
    strncpy( message_buf + 33, "Extern ",7);
    strncpy( message_buf + 43, " Unknown  ", 10 );
    for ( refptr = sym_ext_ref_head ; refptr ; refptr= refptr->next ) {
   i = strlen( refptr->symbol );
   strncpy( message_buf + 1, refptr->symbol, i );
   for ( chptr = message_buf + i + 1 ; i < MAXSYMLEN ; i++, chptr++ )
        *chptr = ' ' ;
   l_addline(l_neither, 0, message_buf , &lptr );
   }

  } /* sym_add_symtabtolisting */



/***************************************************************************
  Function sym_process_unresolved_locals

     This function is meant to be called at the end of assembly.
     It checks the local reference list to see if any unresolved
     symbols are left in it. If there are , it generates an unresolved
     symbol error for each symbol, then deletes the local reference list.

     It also manually places the errors into the listing following the
     lines where the unresolved symbol was referenced, as well as calls
     l_addline to add the error message(s) at the end of the listing.

     Returns the total number of unresolved symbols.

 **************************************************************************/
  int  sym_process_unresolved_locals()
  {
    int           i  ;
    sym_ref_type     * reflistptr ;
    sym_operand_type * refptr ;
    sym_label_type   * glbptr ;
    l_line_type      * lptr , * temp_lptr;
    err_type        * error ;
    char          message_buf[100] ;

    i = 0 ;

    /* Process global label list first */
    for ( glbptr = sym_glb_lab_head ; glbptr ; glbptr= glbptr->next ) {

       if ( glbptr->relative != '?' )   /* if its known then go to next one */
     continue ;

       e_message(0,23, NULL );
       i ++ ;

       error = e_error.errptr ;
       strcpy( message_buf , error->message ) ;
       strcpy( message_buf+strlen(error->message), glbptr->symbol );

       l_addline(l_neither, 0, message_buf , &lptr );

       e_delete_errors() ;
       }

    /* Process the local reference list. */
    for ( refptr = sym_local_ref_head ; refptr ; refptr= refptr->next ) {
       e_message(0,23, refptr->symbol );
       i ++ ;

       error = e_error.errptr ;
       strcpy( message_buf , error->message ) ;
       strcpy( message_buf+strlen(error->message), refptr->symbol );

       l_addline(l_neither, 0, message_buf , &lptr );
       l_addline(l_neither, 0, message_buf , &lptr );

       /* unlink the second redundant line */
       lptr->next->prev = lptr->prev ;
       lptr->prev->next = lptr->next ;

       /* link the second line into listing where symbol was first referenced */
       temp_lptr = refptr->list->termptr->lineptr ;
       lptr->next = temp_lptr->next ;
       lptr->next->prev = lptr ;
       lptr->prev = temp_lptr ;
       temp_lptr->next = lptr ;

       e_delete_errors() ;
       }

    /* delete the local reference list */
    while ( refptr = sym_local_ref_head ) {
   while ( reflistptr = refptr->list ) {
      refptr->list = refptr->list->next ;
      free( reflistptr );
      }
   sym_local_ref_head = refptr->next ;
   free( refptr );
   }

    return i ;

  } /* sym_process_unresolved_locals */


/***************************************************************************
  Function sym_delete_all_tables
     This function deletes all the symbol tables.
  Globals
     sym_glb_label_head, sym_local_lab_head
     sym_ext_ref_head  , sym_local_ref_head   : all set to NULL when
                  the tables are deleted.

 **************************************************************************/
 void sym_delete_all_tables( void )
 {
  sym_operand_type * refptr ;
  sym_label_type   * labptr ;
  sym_ref_type      * reflistptr ;

  /* delete the global label list */
  while ( labptr = sym_glb_lab_head ) {
      sym_glb_lab_head = labptr->next ;
      free( labptr );
      }

  /* delete the local label list */
  while ( labptr = sym_local_lab_head ) {
      sym_local_lab_head = labptr->next ;
      free( labptr );
      }

  /* delete the local reference list */
  while ( refptr = sym_local_ref_head ) {
      while ( reflistptr = refptr->list ) {
    refptr->list = refptr->list->next ;
    free( reflistptr );
    }
      sym_local_ref_head = refptr->next ;
      free( refptr );
      }

  /* delete the external reference list */
  while ( refptr = sym_ext_ref_head ) {
      while ( reflistptr = refptr->list ) {
    refptr->list = refptr->list->next ;
    free( reflistptr );
    }
      sym_ext_ref_head = refptr->next ;
      free( refptr );
      }

 } /* sym_delete_all_tables */


/***************************************************************************
  Function sym_resolve_back

     This function resolves all back references of a particular
     label symbol passed to it.

  Input :
     symptr : pointer to label symbol node with value already resolved.


 **************************************************************************/
  void sym_resolve_back( sym_label_type  *  symptr )
  {
    unsigned int       i  ;
    sym_operand_type * temp, * prev ;
    sym_ref_type     * refptr , * refhead , * tempref ;
    p_sym_type        * sym ;
    am_term_type     * termptr ;
    int           warnings ;

    prev = NULL ;
    for ( temp = sym_local_ref_head ;
     temp && strncmp(temp->symbol,symptr->symbol, MAXSYMLEN ) ;
     prev = temp , temp = temp->next );

    if ( !temp ) return ;
                  /* if there are back references */
    if ( prev )          /* remove the operand node from list.*/
       prev->next = temp->next ;
    else
       sym_local_ref_head = temp->next ;
    refhead = temp->list ;
    free( temp ) ;

    refptr = refhead ;
    while ( refptr ) {
   termptr = refptr->termptr ;
   for ( sym = termptr->symptr ; sym ; sym= sym->next )
       if ( ! strncmp( sym->sym, symptr->symbol, MAXSYMLEN ) ) {
          /* if the symbol is in the symbol list of the term */
          /* then resolve it. */
          sym->val = symptr->val ;

          sym->sym[0] = symptr->relative ;
          sym->sym[1] = 1 ; /* for relative '*' put in count after it */
          }
   refptr = refptr->next ;
   }

    /* compress ref list so that only unique termlists are refered to. */
    refptr = refhead ;
    while ( refptr ) {
   termptr = refptr->termptr ;
   if ( termptr )
   switch ( termptr->class ) {
     case am_first_instr_term :
        /* get rid of any term pointers which refer to the */
        /* next term if its class is am_other_instr   */
        /* this works since instructions can have only 2 terms */
        if ( termptr->next )
      if ( termptr->next->class == am_other_instr_term ) {
         for ( tempref = refhead; tempref ; tempref=tempref->next)
             if ( tempref->termptr == termptr->next )
           tempref->termptr = NULL ;
         }
        break ;
     case am_other_instr_term :
        /* back up to first_instr_term and do same as in case above*/
        if ( termptr->prev )   /* there should always be a prev term */
      if ( termptr->prev->class == am_first_instr_term ) {
         for ( tempref = refhead; tempref ; tempref=tempref->next)
             if ( tempref->termptr == termptr->prev )
           tempref->termptr = NULL ;
         }
        refptr->termptr = termptr->prev ; /* set ptr to first term */
        break ;
     case am_data_term :
        /* resolve only one data term at a time. no compression */
        break;
     }
   refptr = refptr->next ;
   }

    /* resolve terms and dispose of reference list. */
    refptr = refhead ;
    while ( refptr ) {
       termptr = refptr->termptr ;
       tempref = refptr->next ;
       free(refptr);
       refptr = tempref ;
       if ( !termptr ) continue ;

       i=am_resolve_term( termptr,( termptr->class == am_data_term)?1:0);

       if ( !i ) {              /* if all resolved */
     warnings = e_error.warnings ;
     am_backfill( termptr );

     if ( e_error.warnings > warnings )
        l_add_errors( termptr->lineptr );

     warnings = e_error.warnings ;
     am_remove_terms_from_list(&termptr) ;
     }

       }

  } /* sym_resolve_back */



/***************************************************************************
  Function sym_lookup_global

     This function looks up a symbol in the global label list.
     If the symbol is found, it returns a pointer to its label node,
     otherwise it returns NULL.

  Input :
     symbol : pointer to global symbol string.

  Returns :
     described above.

 **************************************************************************/
  sym_label_type * sym_lookup_global( char  *  symbol )
  {
    sym_label_type * temp ;

    for ( temp = sym_glb_lab_head ;
     temp && strncmp(temp->symbol,symbol, MAXSYMLEN ) ;
     temp = temp->next );
    return temp ;

  } /* sym_lookup_global */


/***************************************************************************
  Function sym_add_global

     This function will add a symbol to the global list if the
     symbol is not already in the global list.

     The symbol string is copied into the label node on success.

  Note : The relative field of the label node is set to '?' which
    denotes that the symbol does not yet have a value.

  Input :
     symbol : pointer to the symbol string of MAXSYMLEN or less.

  Globals :
     sym_glb_lab_head : head pointer is updated when symbol is added
         to global list.
  Warnings issued :
     74  : Symbol already in global list. Ignored.

  Returns :
     0 : symbol was added okay.
     41: not enough memory to add to list.

 **************************************************************************/
  int  sym_add_global( char  *   symbol )
  {
   sym_label_type * temp ;

   if ( sym_lookup_global( symbol ) ) {
      e_message(0,74,NULL) ;         /* WARNING symbol already in glb list.*/
      return 0 ;
      }
   if ( temp = ( sym_label_type * ) malloc ( sizeof(sym_label_type) ) ) {
      strncpy( temp->symbol, symbol, MAXSYMLEN );
      temp->relative = '?' ;
      temp->next = sym_glb_lab_head ;
      temp->val = 0L ;
      sym_glb_lab_head = temp ;
      return 0 ;
      }
   e_message(0,41,NULL) ; /* out of memory */
   return 41 ;

  } /* sym_add_global */



/***************************************************************************
  Function sym_lookup_extern

     This function looks up a symbol in the external label list.
     If the symbol is found, it returns a pointer to its operand node,
     otherwise it returns NULL.

  Input :
     symbol : pointer to external symbol string.

  Returns :
     described above.

 **************************************************************************/
  sym_operand_type * sym_lookup_extern( char  *  symbol )
  {
   sym_operand_type * temp ;

   for ( temp = sym_ext_ref_head ;
    temp && strncmp(temp->symbol,symbol, MAXSYMLEN ) ;
    temp = temp->next );
   return temp ;

  } /* sym_lookup_ext */



/***************************************************************************
  Function sym_add_extern

     This function will add a symbol to the external list if the
     symbol is not already in the external list.

     The symbol string is copied into the operand node on success.

  Input :
     symbol : pointer to the symbol string of MAXSYMLEN or less.

  Globals :
     sym_ext_ref_head : reference head pointer is updated when symbol is added
         to external list.
  Warnings issued :
     75  : Symbol already in external list. Ignored.

  Returns :
     0 : symbol was added okay.
     41: not enough memory to add to list.

 **************************************************************************/
  int  sym_add_extern( char  *   symbol )
  {
    sym_operand_type * temp ;

    if ( sym_lookup_extern( symbol ) ) {
       e_message(0,75,NULL) ;      /* WARNING symbol already in extern list.*/
       return 0 ;
       }
    if ( temp = ( sym_operand_type * ) malloc ( sizeof(sym_operand_type) ) ) {
       strncpy( temp->symbol, symbol, MAXSYMLEN );

       temp->next = sym_ext_ref_head ;
       temp->list = NULL ;
       sym_ext_ref_head = temp ;
       return 0 ;
       }
    e_message(0,41,NULL) ; /* out of memory */
    return 41 ;

  } /* sym_add_extern */



/***************************************************************************
  Function sym_lookup_local

     This function looks up a symbol in the local label list.
     If the symbol is found, it returns a pointer to its label node,
     otherwise it returns NULL.

  Input :
     symbol : pointer to local symbol string.

  Returns :
     described above.

 **************************************************************************/
  sym_label_type * sym_lookup_local( char  *  symbol )
  {
    sym_label_type * temp ;

    for ( temp = sym_local_lab_head ;
     temp && strncmp(temp->symbol,symbol, MAXSYMLEN ) ;
     temp = temp->next );
    return temp ;

  } /* sym_lookup_local */



/***************************************************************************
  Function sym_add_label_symbol

     This function will add a label symbol to a local label list,
     or resolve an already defined global label following the
     algorithm :

    if label already in extern label list then error
    else
       if label already in global label list then
          if its already resolved then error
          else
        resolve it and all back references in local op list.
       else
          if label in local label list then error
          else
       resolve it and all back references in local op list.

     If the relative field is set to am_relative then the label is
     treated as a relative label, otherwise it is treated as an
     absolute label.

  Note : The relative field is set to '*' if relative or 0 for absolute.

  Input :
     symbol   : pointer to the symbol string of MAXSYMLEN or less.
     val      : the long value of the symbol.
     relative : specifies whether or not to treat the label as relative.

  Globals :
     sym_local_lab_head : Local label head pointer is updated when symbol is
           added to label list.

  Returns :
     0 : okay.
     17: Cannot resolve an external symbol locally.
     18: symbol was already resolved.
     41: not enough memory to add to list.

 **************************************************************************/
  int  sym_add_label_symbol ( char       * symbol ,
               unsigned long   val    ,
               am_assem_type   relative )
  {
    sym_label_type  * temp ;

    if ( sym_lookup_extern( symbol ) ) {
       e_message(0,17,NULL) ;      /* label symbol already in extern list.*/
       return 17;         /* cannot resolve locally. */
       }

    if ( temp = sym_lookup_global( symbol ) ) {   /* in global list */
       if ( temp->relative == '?' ) {  /* not resolved yet. */
     temp->relative = ( relative == am_relative ) ? '*' : 0 ;
     temp->val = val ;
                /* resolve back references */
     sym_resolve_back( temp );
     return 0 ;
     }
       else {
     e_message(0,18,NULL) ;  /* symbol already resolved */
     return 18 ;
     }
       }

    if ( sym_lookup_local( symbol ) ) {
       e_message(0,18,NULL) ;      /* symbol already resolved in local list.*/
       return 18 ;
       }

    if ( temp = ( sym_label_type * ) malloc( sizeof(sym_label_type)) ) {
       strncpy( temp->symbol, symbol, MAXSYMLEN );
       temp->relative = ( relative == am_relative ) ? '*' : 0 ;
       temp->next = sym_local_lab_head ;
       temp->val  = val ;
       sym_local_lab_head = temp ;
               /* resolve back references */
       sym_resolve_back( temp );
       return 0 ;
       }
    else {
       e_message(0,41,NULL) ;  /* out of memory */
       return 41 ;
       }

  } /* sym_add_label_symbol */



/***************************************************************************
  Function sym_add_operand_symbol

     This function will add a attempt to resolve a symbol contained
     within a symbol node, or set the reference pointers in the
     reference lists accordingly depending on whether or not the
     symbol's value is known. It follows this algorithm :

    if symbol already in extern label list then
       add reference to it into the extern list.
    else
       if symbol already in global label list then
          if its already resolved then
        resolve it
          else
        add reference to the symbol to local op list.
       else
          if symbol in local label list then
        resolve it
          else
        add reference to the symbol to local op list.

  Input :
     symptr   : pointer to the symbol node.
     termptr  : pointer to term list that contains the symbol node.
     addref   : boolean flag indicating whether or not a reference
      pointer should be set up if the symbol is not yet
      resolved.
       if TRUE then reference pointers will be set up.
       if FALSE then reference pointers will not be set up.
  Note   : If addref is set to FALSE then no error can result since
     no memory allocation is attempted. Therefore the caller
     need not check the return code.
  Calls :
     sym_addtolocaloplist : This adds the refernece pointers for later
             resolution to the local operand list.

  Returns :
     0 : okay.
     41: not enough memory to add to list.

 **************************************************************************/
  int  sym_add_operand_symbol ( p_sym_type    * symptr   ,
            am_term_type  * termptr ,
            char      addref   )
  {
    sym_operand_type * temp  ;
    sym_ref_type     * temp2 ;
    sym_label_type   * temp3 ;

    if ( temp = sym_lookup_extern( symptr->sym ) ) {  /* in extern list */
       if ( ! addref ) return 0 ;
       if ( temp2 = ( sym_ref_type * ) malloc( sizeof(sym_ref_type)) ) {
     temp2->termptr = termptr ;
     temp2->next = temp->list ;
     temp->list = temp2 ;
     return 0 ;
     }
       else {
    e_message(0,41,NULL) ;   /* not enough memory */
    return 41 ;
    }
       }

    if ( temp3 = sym_lookup_global( symptr->sym ) ) {   /* in global list */
       if ( temp3->relative == '?' )   /* not resolved yet. */
     if ( ! addref ) return 0 ;
     else return sym_addtolocaloplist( symptr, termptr ) ;
       else {
     symptr->val = temp3->val ;            /* resolve the symbol */
     symptr->sym[0] = temp3->relative ;
     symptr->sym[1] = 1 ; /* set relative count to 1 */
     return 0 ;
     }
       }

    if ( temp3 = sym_lookup_local( symptr->sym ) ) {
       symptr->val = temp3->val ;           /* resolve the symbol*/
       symptr->sym[0] = temp3->relative ;
       symptr->sym[1] = 1 ;   /* set relative count to 1 */
       return 0 ;
       }
    else
       if ( ! addref ) return 0 ;
       else return sym_addtolocaloplist( symptr, termptr ) ;

  } /* sym_add_operand_symbol */



/***************************************************************************
  Function sym_addtolocaloplist

     This function will add a symbol reference to the local operand
     list. If the symbol already has referneces in the local operand
     list, it creates a new reference node only. If the symbol has no
     previous references, it creates the symbol operand node as well
     as its first reference node.

  Input :
     symptr   : pointer to the symbol node.
     termptr  : pointer to term list that contains the symbol node.

  Globals :
     sym_local_ref_head : updated when symbol has never been referenced
           before, and a new operand node was created.

  Returns :
     0 : okay.
     41: not enough memory to add to list.

 **************************************************************************/
  int  sym_addtolocaloplist ( p_sym_type    * symptr  ,
               am_term_type  * termptr )
  {
    sym_operand_type  * temp ;
    sym_ref_type      * temp2 ;

    for ( temp = sym_local_ref_head ;
     temp && strncmp(temp->symbol,symptr->sym, MAXSYMLEN ) ;
     temp = temp->next );
    if ( temp )
       if ( temp2 = ( sym_ref_type * ) malloc( sizeof(sym_ref_type)) ) {
     temp2->termptr = termptr ;
     temp2->next = temp->list ;
     temp->list = temp2 ;
     return 0 ;
     }
       else {
     e_message(0,41,NULL) ; /* not enough memory */
     return 41 ;
     }
    else
       if ( temp = ( sym_operand_type * ) malloc (sizeof(sym_operand_type)) )
     if ( temp2 = ( sym_ref_type * ) malloc( sizeof(sym_ref_type)) ) {
        strncpy( temp->symbol, symptr->sym, MAXSYMLEN );
        temp->next = sym_local_ref_head ;
        temp->list = temp2 ;
        sym_local_ref_head = temp ;
        temp2->termptr = termptr ;
        temp2->next = NULL ;
        return 0 ;
        }
     else {
        free(temp);
        e_message(0,41,NULL);  /* not enough memory */
        return 41 ;
        }
       else {
     e_message(0,41,NULL);  /* not enough memory */
     return 41 ;
     }


  } /* sym_addtolocaloplist */










Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.