home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1990 / 09 / ives.asc < prev    next >
Text File  |  1990-07-25  |  95KB  |  3,245 lines

  1. _A GENERIC ONE-PASS ASSEMBLER_
  2. by William E. Ives
  3.  
  4. [LISTING ONE]
  5.  
  6.  
  7. /****************************************************************************
  8.  
  9.   Main driver for generic assembler.
  10.   Copyright 1988 by Michigan Technological University
  11.  
  12.   Written by : William E. Ives
  13.  
  14.   Version : 1.0
  15.   Date      : Feb 1, 1989
  16.  
  17.  ****************************************************************************/
  18.  
  19. #include <stdio.h>
  20. #include <conio.h>
  21. #include <string.h>
  22. #include <time.h>
  23. #include <dir.h>
  24.  
  25. #include "68defs.h"
  26. #include "68err.h"
  27. #include "68parse.h"
  28. #include "68list.h"
  29. #include "68assem.h"
  30. #include "68symtab.h"
  31.  
  32. void assembler_print_errors ( char * message , char * add_mess )
  33. {
  34.  printf(" %s %s \n",message,add_mess );
  35. }
  36.  
  37. main()
  38. {
  39.  int      error_count, warning_count ;
  40.  FILE    * outfile ;
  41.  FILE    * in_file ;
  42.  char      fn[MAXPATH],outname[MAXPATH], temp[MAXPATH], *ptr;
  43.  
  44.  /* Following used to parse a path into its components. */
  45.  char drive[MAXDRIVE] , dir[MAXDIR], file[MAXFILE], ext[MAXEXT];
  46.  
  47.  error_count = 0 ;
  48.  warning_count = 0 ;
  49.  
  50.  e_printf = assembler_print_errors ;
  51.  
  52.  puts(" Generic Assembler. Version 1.0\n");
  53.  puts(" Written by : William E. Ives");
  54.  puts(" Copyright (c) 1988 by Michigan Technological University.\n");
  55.  
  56.  printf(" Absolute or Relative assembly ? (A/R) ");
  57.  fn[0] = getche();
  58.  putchar('\n');
  59.  am_assem_class = (( fn[0] == 'a')||(fn[0] == 'A')) ?
  60.           am_absolute : am_relative ;
  61.  
  62.  printf(" Enter source file name [.ASM] =>");
  63.  fn[0]=MAXPATH-1;
  64.  ptr=cgets(fn);
  65.  strcpy(fn,ptr);
  66.  putchar('\n');
  67.  
  68.  fnsplit( fn,drive,dir,file,ext);
  69.  
  70.  /* assign list file name.*/
  71.  
  72.  if ( ! ( *ext ) )
  73.     fnmerge( fn, drive,dir,file,".ASM");
  74.  
  75.  fnmerge( outname,drive,dir,file,".LST");
  76.  
  77.  printf(" Enter name of list file [%s] =>",outname);
  78.  temp[0]=MAXPATH-1;
  79.  ptr = cgets(temp);
  80.  putchar('\n');
  81.  if ( temp[1] ) strcpy(outname,ptr);
  82.  
  83.  in_file = fopen( fn,"r");
  84.  if ( in_file == NULL ) {
  85.     e_message(0,30, fn );
  86.     return 30 ;
  87.     }
  88.  
  89.  puts(" Assembling..");
  90.  
  91.  e_hold_messages = TRUE ;
  92.  am_pass1( in_file , &error_count, &warning_count );
  93.  e_hold_messages = FALSE ;
  94.  
  95.  fclose(in_file);
  96.  
  97.  printf(" Total Errors %d    Total Warnings %d \n",
  98.       error_count, warning_count );
  99.  
  100.  puts(" Writing listing file.\n");
  101.  outfile = fopen( outname,"w");
  102.  if ( outfile == NULL ) {
  103.     e_message(0,30, outname );
  104.     return 30 ;
  105.     }
  106.  l_printlisting( outfile ,TRUE );
  107.  fprintf(outfile,"\n Total Errors %d    Total Warnings %d \n",
  108.          error_count, warning_count );
  109.  
  110.  fprintf(outfile,"\n\n Name of file :%s\n",fn);
  111.  fclose(outfile);
  112.  
  113.  return 0 ;
  114.  
  115. } /* main */
  116.  
  117.  
  118.  
  119. [LISTING TWO]
  120.  
  121.  
  122. /*
  123.      68000 Assembly Module.
  124.      This module contains those procedures needed to handle
  125.      assembly.
  126. */
  127.  
  128.   #include <stdio.h>
  129.   #include <stdlib.h>
  130.   #include <string.h>
  131.   #include <ctype.h>
  132.  
  133.   #include "68defs.h"
  134.   #include "68err.h"
  135.   #include "68parse.h"
  136.   #include "68list.h"
  137.   #include "68assem.h"
  138.   #include "68symtab.h"
  139.   #include "68pseudo.h"
  140.   #include "68instr.h"
  141.  
  142.   #define LINELEN 80
  143.   #define LOCAL near pascal
  144.  
  145.   void p_assem_line ( char        * line        ,
  146.               char          label[MAXSYMLEN]    ,
  147.               char          command[MAXSYMLEN],
  148.               p_size_type   * size        ,
  149.               char        * numterms        ,
  150.               am_term_type ** termlist        ) ;
  151.  
  152.  
  153.  unsigned long int    am_location_counter = 0L      ;
  154.  char              am_end_found      = FALSE ;
  155.  char              am_trunc_lines      = TRUE  ;
  156.  
  157.  /* These globals are used for relative symbol/term resolution by linker*/
  158.  unsigned long int    am_relbase      = 0L      ;
  159.  char              am_relknown      = FALSE ;
  160.  
  161.  
  162.               /* size of absolute address in words. */
  163.               /* 1 - abs short, 2 - abs long.    */
  164.  char              am_abs_address_size  = 1;
  165.  
  166.  am_term_type        * am_term_list_head = NULL , * am_term_list_tail = NULL ;
  167.  am_assem_type          am_assem_class    = am_absolute ;
  168.  
  169.  
  170.  
  171. /*****************************************************************************
  172.    Function am_resolve_symbol
  173.  
  174.      This function resolves a symbol list if it is possible
  175.      If the symbol list is resolved it deletes every symbol
  176.      node but the first one which contains the sum.
  177.      It also compresses the symbol list as much as possible even
  178.      if all symbol are not resolved.
  179.        It compresses relative symbols by maintaining a count of
  180.      relative symbols.    It adds or subtracts from this count
  181.      according to the operator for the symbol.    If when the list
  182.      is fully compressed, the relative count is not zero, then
  183.     if the relative base is known, the final sum is
  184.        computed by adding in relcount*am_relbase
  185.     else
  186.        relflag '*' is put into symbol[0]  and
  187.        relcount is put into symbol[1] of the first symbol node.
  188.  
  189.      Globals
  190.     am_relknown : flag indicating that relative symbol base value is
  191.               known.
  192.     am_relbase  : the known relative base.
  193.      Variable parameters :
  194.     symlist : the symbol list.
  195.  
  196.      Return value
  197.     The number of symbols still left unresolved.
  198.  ****************************************************************************/
  199.  int am_resolve_symbol( p_sym_type * symlist )
  200.  {
  201.    register int   symcount = 0 ;
  202.    unsigned long  sum = 0L ;
  203.    char       rel , relcount = 0 ;
  204.    p_sym_type    * symbol , * temp , * prev ;
  205.  
  206.    temp = NULL ;
  207.    prev = NULL ;
  208.    symbol = symlist ;
  209.    while ( symbol ) {
  210.  
  211.       rel = ( symbol->sym[0] == '*'  ) ;  /* set relative flag.*/
  212.  
  213.       if ( !rel && symbol->sym[0]  ) {        /* if there is a symbol.*/
  214.     symcount++ ;
  215.     prev = symbol ;
  216.     symbol = symbol->next ;
  217.     }
  218.       else {              /* there is a value. perform calculations */
  219.     if ( symbol->operator == '+' ) {
  220.        if ( rel ) relcount += symbol->sym[1] ;
  221.        sum += symbol->val ;
  222.        }
  223.     else {
  224.        if ( rel ) relcount -= symbol->sym[1] ;
  225.        sum -= symbol->val ;
  226.        symbol->operator = '+';
  227.        }
  228.  
  229.     if ( !temp ){          /* if temp == null */
  230.        temp = symbol ;
  231.        prev = symbol ;
  232.        symbol = symbol->next ;
  233.        }
  234.     else {
  235.        prev->next = symbol->next ;
  236.        free(symbol);
  237.        symbol = prev->next ;
  238.        }
  239.     }
  240.       }
  241.  
  242.    /* if there were no unresolved symbols but relatives didn't cancel then*/
  243.    if ( !symcount && relcount )
  244.       if ( am_relknown ) {
  245.      sum += ( relcount * am_relbase ) ;   /* resolve relative if known.*/
  246.      relcount = 0;
  247.      }
  248.       else
  249.      /* set symcount to 1 so that caller knows that chain is not resolved.*/
  250.      symcount=1 ;
  251.  
  252.    if ( temp ) {
  253.       temp->val = sum ;
  254.       temp->operator = '+' ;
  255.       if ( !relcount ) temp->sym[0] = 0 ;
  256.       else {
  257.      temp->sym[0] = '*' ;
  258.      temp->sym[1] = relcount ;
  259.      }
  260.       }
  261.    if ( prev )    prev->next = NULL ;
  262.  
  263.    return symcount ;
  264.  
  265.  } /* am_resolve_symbol */
  266.  
  267.  
  268.  
  269. /*****************************************************************************
  270.    Function am_resolve_term
  271.  
  272.      This function resolves a term list if it is possible.  It only
  273.      resolves the terms until any the following conditions occur :
  274.      - the first term class was am_first_instr_term and
  275.        the next term is not am_other_instr_term
  276.      - the terms classes are all am_data_terms and the
  277.        datatermcount has been reached or am_first_instr_term
  278.        has been reached.
  279.      - the next term is NULL
  280.  
  281.  
  282.      Variable parameters :
  283.     termlist : The term list
  284.     datatermcount : The number of data terms to resolve if the
  285.             term class is am_data_term.
  286.             This parameter is ignored if the first term
  287.             was am_first_instr_term.
  288.      Return value
  289.     The number of terms still left unresolved.
  290.  ****************************************************************************/
  291.  int am_resolve_term ( am_term_type * termlist , char    datatermcount )
  292.  {
  293.    register int    termcount = 0 ;
  294.    am_term_type  * term ;
  295.    char        okay , count ;
  296.  
  297.    term = termlist ;
  298.    okay = TRUE ;
  299.    count = 1 ;
  300.    while ( term && okay ) {
  301.       if ( am_resolve_symbol(term->symptr ) ) termcount++ ;
  302.       term = term->next ;
  303.       count++ ;
  304.       if ( term )
  305.      okay = ( termlist->class == am_first_instr_term &&
  306.            term->class == am_other_instr_term ) ||
  307.         ( termlist->class == am_data_term &&
  308.           term->class == am_data_term  &&
  309.           count       <= datatermcount    ) ;
  310.       }
  311.  
  312.    return termcount ;
  313.  
  314.  } /* am_resolve_term */
  315.  
  316.  
  317.  
  318. /*****************************************************************************
  319.    Function am_delete_terms
  320.  
  321.       If the input parameter 'ALL' is set to TRUE (1) then
  322.     this function frees all terms and associated symbols in a term list
  323.     until null is encountered.
  324.     The variable 'termlist' is set to NULL upon completion.
  325.       If the input parameter 'ALL' is set to FALSE (0) then
  326.     this function frees only one term and associated symbols.
  327.     The variable 'termlist' is set to 'termlist->next' upon completion.
  328.  
  329.    Input
  330.        all : boolean flag indicating how many terms to delete ( see above )
  331.    Variable parameter
  332.        termlist : a pointer variable which points to the termlist.
  333.  
  334.  
  335.  *****************************************************************************/
  336.  void am_delete_terms ( am_term_type ** termlist , char   all )
  337.  {
  338.   am_term_type * term ;
  339.   p_sym_type   * sym  ;
  340.  
  341.   term = *termlist ;
  342.   if ( all ) {
  343.      while ( term = *termlist ) {
  344.  
  345.        if (  ((term->modereg >> 8) == 7) &&
  346.          ((term->modereg & 15)==10 ) ) {
  347.       free(term->symptr);             /* free the string. */
  348.       term->symptr = NULL ;
  349.       }
  350.  
  351.        while ( sym = term->symptr ) {
  352.       term->symptr = term->symptr->next ;
  353.       free(sym) ;
  354.       }
  355.        *termlist = term->next ;
  356.        free(term);
  357.        }
  358.     }
  359.   else {
  360.     if ( term ) {
  361.  
  362.       if (  ((term->modereg >> 8) == 7) &&
  363.         ((term->modereg & 15)==10 ) ) {
  364.      free(term->symptr);            /* free the string. */
  365.      term->symptr = NULL ;
  366.      }
  367.  
  368.       while ( sym = term->symptr ) {
  369.      term->symptr = term->symptr->next ;
  370.      free(sym) ;
  371.      }
  372.       *termlist = term->next ;
  373.       free(term);
  374.       }
  375.     }
  376.  
  377.  } /* am_delete_terms */
  378.  
  379.  
  380.  
  381. /*****************************************************************************
  382.    Procedure am_add_terms_to_list
  383.  
  384.       This procedure links the list of terms into the global list
  385.       of terms.
  386.       This global list is used for all terms associated with data
  387.       or instructions which contain forward/external references which
  388.       are not yet resolved.
  389.  
  390.    Note : Term list is implemented as a non-circular doubly linked list.
  391.  
  392.    Globals :
  393.       am_term_list_head : points to the head of the term list.
  394.       am_term_list_tail : points to the tail of the term list.
  395.  
  396.    Variable parameter
  397.        termlist : A pointer variable which points to the termlist to
  398.           be linked in.  It is set to NULL when all the terms
  399.           are transfered.
  400.  
  401.  
  402.  *****************************************************************************/
  403.  void am_add_terms_to_list ( am_term_type ** termlist  )
  404.  {
  405.   am_term_type * term ;
  406.  
  407.   if ( ! (*termlist) ) return ;       /* if NULL then leave. */
  408.  
  409.   if ( !am_term_list_tail )  {          /* list is empty */
  410.      am_term_list_head = *termlist ;
  411.      am_term_list_head->prev = NULL ;
  412.      }
  413.   else {
  414.      am_term_list_tail->next = *termlist ;
  415.      am_term_list_tail->next->prev = am_term_list_tail ;
  416.      }
  417.  
  418.   for ( term = *termlist ; term ; term = term->next )  /* set tail */
  419.       am_term_list_tail = term ;
  420.  
  421.   *termlist = NULL;
  422.   return ;        /* return used to avoid compilier warning. */
  423.  
  424.  } /* am_add_terms_to_list */
  425.  
  426.  
  427.  
  428. /*****************************************************************************
  429.    Procedure am_remove_terms_from_list
  430.  
  431.       This procedure removes the links from the global list of terms
  432.       associated with the passed in termptr . It then deletes the terms
  433.       by calling am_delete_terms.
  434.       If the termptr points to a term with class am_first_instr_term
  435.       then this routine will remove all terms for the instruction.
  436.       If the termptr points to a term with class am_data_term
  437.       then this routine will ONLY the ONE term.
  438.  
  439.    Note : Term list is implemented as a non-circular doubly linked list.
  440.  
  441.    Globals :
  442.       am_term_list_head : points to the head of the term list.
  443.       am_term_list_tail : points to the tail of the term list.
  444.  
  445.    Variable parameter
  446.        termlist : A pointer variable which points to the termlist to
  447.           be removed.  It is set to NULL when all the terms
  448.           are deleted.
  449.  
  450.  *****************************************************************************/
  451.  void am_remove_terms_from_list ( am_term_type ** termlist  )
  452.  {
  453.   am_term_type * term ;
  454.   char         i    ;
  455.  
  456.   if ( ! (*termlist) ) return ;       /* if NULL then leave. */
  457.  
  458.   term = *termlist ;
  459.   i = 1 ;
  460.   if ( term->class == am_first_instr_term ) {
  461.      if ( term->next )
  462.     if ( term->next->class == am_other_instr_term )
  463.                      /* remove both instr terms*/
  464.        i = 2 ;
  465.      }
  466.  
  467.   if ( term == am_term_list_head )
  468.      am_term_list_head = ( i == 1 ) ? term->next : term->next->next ;
  469.   else
  470.      term->prev->next = ( i == 1 ) ? term->next : term->next->next ;
  471.  
  472.   if ( ( term == am_term_list_tail ) ||
  473.        ( ( i == 2 ) && ( term->next == am_term_list_tail )) )
  474.      am_term_list_tail = term->prev  ;
  475.   else
  476.     if ( i == 1 )
  477.       term->next->prev = term->prev ;
  478.     else
  479.       if ( term->next->next ) term->next->next->prev = term->prev ;
  480.  
  481.   if ( i == 1 )
  482.      term->next = NULL ;
  483.   else
  484.      term->next->next = NULL ;
  485.  
  486.   am_delete_terms( &term, TRUE );
  487.  
  488.   *termlist = NULL;
  489.   return ;        /* return used to avoid compilier warning. */
  490.  
  491.  } /* am_remove_terms_from_list */
  492.  
  493.  
  494. /****************************************************************************
  495.     Procedure am_backfill
  496.  
  497.       This procedure updates the fields within the opcode.
  498.       It determines actual post words for entire instruction, and
  499.       places the opcode and post words into the listing.
  500.  
  501.       It expects all terms and symbols to be resolved.
  502.  
  503.       Input Parameter :
  504.       termlist - will be resolved when finished creating final opcode.
  505.  
  506.   ***************************************************************************/
  507.   void    am_backfill(  am_term_type  * termlist )
  508.  
  509.   {
  510.    char        i,j,k, reg     ;
  511.    am_term_type *  term ;
  512.    unsigned int    opcode ;
  513.    l_line_type    *  lptr ;
  514.  
  515.    lptr = termlist->lineptr ;
  516.    opcode = termlist->opcode;    /* get opcode */
  517.  
  518.    /* write opcode to listing */
  519.    l_writetoline(1,opcode,0,lptr);
  520.  
  521.    term = termlist ;
  522.    j =    ( term->index == 0 ) ? 2 : 1 ;
  523.    reg = ( opcode & 7 ) ;
  524.  
  525.    for (i=1,k=2 ; i <= j && term ; i++ ) {
  526.      switch ( reg ) {
  527.        case 1 :     /* abs.l */
  528.       l_writetoline(k,term->symptr->val>>16,0,lptr);
  529.       k++ ;
  530.        case 0 :     /* abs.w */
  531.       l_writetoline(k,term->symptr->val & 0xFFFF ,0,lptr);
  532.       k++ ;
  533.       break ;
  534.        }
  535.       term = term->next ;
  536.       reg = ((opcode>>9)&7) ;
  537.      }
  538.  
  539.   } /* am_backfill */
  540.  
  541.  
  542. /*****************************************************************************
  543.    Function am_readln
  544.  
  545.      This function reads in one source line from a file until the end-of-line
  546.      or the end-of-file is encountered.
  547.  
  548.        - it only read up to 'linelen' number of charcters and discards
  549.      the rest of the line.
  550.        - expands TABS to 8 space charcters.
  551.        - returns a blank flag indicating if the first 'linelen' characters
  552.      are blank or not.
  553.        - builds a separate string the same as the first but in uppercase
  554.      in parameter line1.
  555.       NOTE : characters within single or double qoutes are not affected.
  556.  
  557.      Typical calling method : ( for echo of exact file contents )
  558.        while   ( readln(in_file,LINELEN-1,line,line1,&blank) != EOF )
  559.           printf("%s\n",line);
  560.  
  561.      Input
  562.      in_file  : the file to be read.
  563.      linelen  : the max number of characters to be read not counting NULL.
  564.      Variable
  565.      line      : the line read in.
  566.      line1      : the line read in converted to uppercase
  567.      blank      : flag indicating whether or not the line is blank.
  568.  
  569.      Returns
  570.        0 : line read okay.
  571.        EOF : end of file was encountered.
  572.  
  573.  ****************************************************************************/
  574. static int LOCAL am_readln ( FILE * infile, int linelen,
  575.                  char * line  , char * line1, char * blank )
  576. {
  577.    int     ch, ch1 , i  ;
  578.    char  dqoute, sqoute ; /* flags for tracking double & single quotes */
  579.  
  580.    /* if either flag is true then no conversion of case will take place.*/
  581.    dqoute = sqoute = FALSE ;
  582.    *blank = TRUE ;
  583.    i = 0 ;
  584.    while ( ch=ch1=fgetc(infile) ) {
  585.       if ( ch == '\t' ) {                 /* expand tabs into 8 spaces */
  586.      for ( ch = 0 ; ch < 8 ; ch ++, i++ ) {
  587.         if ( i < linelen ) {
  588.            line[i] = line1[i] = ' ';
  589.            line[i+1] = line1[i+1]= NULL;
  590.            }
  591.         }
  592.      continue ;
  593.      }
  594.  
  595.       if ( !isprint(ch) ) break ;
  596.  
  597.       if ( ch == '\'' ) { if ( !dqoute ) sqoute = !sqoute ; }
  598.       else if ( ch == '"' ) { if ( !sqoute ) dqoute = !dqoute ; }
  599.       else if (!( dqoute || sqoute ))  ch1 = toupper(ch) ;
  600.  
  601.       if ( i< linelen ) {
  602.       line[i]= ch ;
  603.       line1[i]= ch1 ;
  604.       if ( *blank ) *blank = ( ch == ' ' );
  605.       line[i+1] = line1[i+1] = NULL ;
  606.       }
  607.  
  608.       i++;
  609.       }
  610.  
  611.    if ( i )   return 0 ;
  612.    return ch ;
  613.  
  614. } /* am_readln */
  615.  
  616.  
  617. /*****************************************************************************
  618.    Function am_pass1
  619.  
  620.      This function assembles an entire file and creates the listing
  621.      and associted data structures as it does so.
  622.  
  623.      Input parameters :
  624.         in_file  : the file containing the source assembly code
  625.                which has already been opened for reading.
  626.  
  627.  
  628.  ****************************************************************************/
  629.  
  630.  void am_pass1 ( FILE * in_file , int  * error_count , int  * warning_count )
  631.  
  632.  {
  633.   int           i  ;
  634.   ps_pseudos       pseudo_class ;
  635.   char           label[MAXSYMLEN], command[MAXSYMLEN] ;
  636.   char           sizeinwords , blank ;
  637.   p_size_type       size  ;
  638.   p_sym_type     * sym     ;
  639.   char           numterms ;
  640.   am_term_type     * termlist, *term ;
  641.   unsigned int       opcode ;
  642.   unsigned int       index  ;
  643.   char           prev_warnings = 0 ;
  644.   char           line[LINELEN], linehold[LINELEN] ;
  645.   l_line_type     * lptr  ;
  646.  
  647.  
  648.   e_error.state = 0 ;
  649.   e_error.warnings = 0 ;
  650.  
  651.   while ( !am_end_found && !e_error.out_of_memory &&
  652.       ( am_readln(in_file,LINELEN-1,line,linehold,&blank) != EOF )) {
  653.  
  654.      if ( blank ){                      /* skip blank lines */
  655.     l_addline( l_neither, 0, "", &lptr );
  656.     continue ;
  657.     }
  658.  
  659.      if ( line[0] == '*' || line[0] == ';' ) {   /* skip comment lines */
  660.     l_addline( l_neither, 0, line, &lptr );
  661.     continue ;
  662.     }
  663.  
  664.      *error_count += e_error.state ;  /* count up total number of errors */
  665.      e_error.state = 0 ;
  666.      label[0]    = 0 ;
  667.      command[0] = 0 ;
  668.      size    = p_unknown ;
  669.      numterms    = 0 ;
  670.      sizeinwords= 0 ;
  671.      termlist    = NULL ;
  672.  
  673.      p_assem_line ( linehold, label, command, &size, &numterms, &termlist );
  674.  
  675.      if ( e_error.state ) {
  676.     /* add errors to listing and go on to next line */
  677.     l_addline( l_neither, 0, line, &lptr);
  678.     am_delete_terms(&termlist, TRUE );
  679.     l_add_errors(lptr);
  680.     continue ;      /* skip to next source line. */
  681.     }
  682.  
  683.      /* if command is psuedo then handle it. */
  684.      if ( ps_lookup_pseudo ( command, &pseudo_class ) ) {
  685.     ps_pseudo( label, pseudo_class, numterms, &termlist, line);
  686.     if ( ( e_error.warnings > prev_warnings ) || e_error.state )
  687.        l_add_errors(l_line_head->prev);
  688.     prev_warnings = e_error.warnings ;
  689.     continue ;   /* skip to next source line. */
  690.     }
  691.  
  692.      if ( am_location_counter & 1 ) {
  693.     e_message(0,22,NULL);          /* location counter is odd */
  694.     l_addline(l_neither, 0, line, &lptr);
  695.     prev_warnings = e_error.warnings ;
  696.     l_add_errors(lptr);
  697.     am_location_counter++; /* adjust counter so this error does not repeat*/
  698.     continue ;
  699.     }
  700.  
  701.      /* its either an instruction or an error */
  702.  
  703.      if ( numterms > 2 ) {
  704.     e_message(0,15,NULL) ;              /* too many terms on line.*/
  705.     l_addline(l_neither, 0, line, &lptr);
  706.     prev_warnings = e_error.warnings ;
  707.     l_add_errors(lptr);
  708.     continue ;
  709.     }
  710.  
  711.      /* set size and initial opcode. */
  712.      i = 1 ;
  713.      switch ( numterms ) {
  714.       case 0 :            /* make source and dest empty */
  715.        i = is_validate ( &size , command, 7, 5, 7, 5,
  716.             &opcode , &sizeinwords, termlist , &index );
  717.  
  718.        break;
  719.       case 1 :            /* make source empty */
  720.        i = is_validate ( &size , command, 7, 5,
  721.             termlist->modereg >> 8,
  722.             termlist->modereg & 15,
  723.             &opcode , &sizeinwords, termlist , &index );
  724.  
  725.        break;
  726.       case 2 :
  727.        i = is_validate ( &size , command,
  728.             termlist->modereg >> 8,
  729.             termlist->modereg & 15,
  730.             termlist->next->modereg >> 8,
  731.             termlist->next->modereg & 15,
  732.             &opcode , &sizeinwords, termlist , &index );
  733.  
  734.        break;
  735.       }
  736.  
  737.      if ( i ) {
  738.        /* error occured while validating. attach it to listing and
  739.       go on to next line */
  740.        l_addline(l_neither, 0, line, &lptr);
  741.        prev_warnings = e_error.warnings ;
  742.        l_add_errors(lptr);
  743.        continue ;
  744.        }
  745.  
  746.      /* do assembly if possible */
  747.  
  748.      /* process line label if there is one */
  749.      if ( label[0] )
  750.        if (sym_add_label_symbol(label,am_location_counter,am_assem_class)){
  751.       l_addline(l_neither, 0, line, &lptr);
  752.       l_add_errors(lptr);
  753.       prev_warnings = e_error.warnings ;
  754.       am_delete_terms(&termlist, TRUE) ;
  755.       continue ;              /* skip to next source line. */
  756.       }
  757.  
  758.      /* resolve already known symbols */
  759.      for ( term = termlist ; term ; term = term->next )
  760.        for ( sym = term->symptr ; sym ; sym = sym->next )
  761.       if ( sym->sym[0] )
  762.          sym_add_operand_symbol( sym, term, TRUE );
  763.  
  764.  
  765.      l_addline( l_firstinstr, sizeinwords, line, &lptr    );
  766.  
  767.      if ( e_error.out_of_memory ) {
  768.     am_delete_terms(&termlist,TRUE);
  769.     break ;
  770.     }
  771.  
  772.      /* its either an instruction or an error */
  773.      if ( e_error.warnings > prev_warnings )
  774.     l_add_errors(lptr);
  775.      prev_warnings = e_error.warnings ;
  776.  
  777.      if (termlist) {
  778.     termlist->lineptr = lptr ;       /* hold onto line.*/
  779.     if ( termlist->next )
  780.        termlist->next->lineptr = lptr ;
  781.     termlist->index   = index     ;  /* hold onto index.*/
  782.     termlist->opcode  = opcode    ;  /* hold onto opcode.*/
  783.     }
  784.  
  785.      l_writetoline(0,am_location_counter,0,lptr);
  786.      l_writetoline(sizeinwords,0,3,lptr);      /* put '?' in listing.*/
  787.  
  788.      /* are all terms knowm? */
  789.      if ( ! am_resolve_term(termlist,0) ) {
  790.     am_backfill( termlist );
  791.  
  792.     if ( e_error.warnings > prev_warnings )
  793.        l_add_errors(lptr);
  794.     prev_warnings = e_error.warnings ;
  795.  
  796.     am_location_counter += ( sizeinwords << 1 ) ;
  797.     am_delete_terms(&termlist, TRUE) ;
  798.     }
  799.      else {
  800.     am_add_terms_to_list( &termlist );
  801.     am_location_counter += ( sizeinwords << 1 ) ;
  802.     }
  803.  
  804.      }
  805.  
  806.   if ( e_error.out_of_memory ) {
  807.     l_add_errors(l_line_head->prev);
  808.     *error_count += e_error.state ;
  809.     }
  810.  
  811.   /* Make sure all local and global symbols are resolved */
  812.   e_error.state = 0 ;
  813.   *error_count +=  sym_process_unresolved_locals() ;
  814.  
  815.   *warning_count = e_error.warnings ;
  816.  
  817.   /* Add symbol table to listing */
  818.   sym_add_symtabtolisting();
  819.  
  820.  } /* am_pass1 */
  821.  
  822.  
  823. [LISTING THREE]
  824.  
  825. /*
  826.   68000 error handler module
  827.  
  828. */
  829.  
  830. #include <stdio.h>
  831. #include <stdlib.h>
  832. #include <string.h>
  833.  
  834. #include "68defs.h"
  835. #include "68err.h"
  836.  
  837.  
  838.  struct e_struct e_error = { 0,0,0,0,FALSE, NULL, NULL };
  839.  
  840.  char e_hold_messages = TRUE ;
  841.  e_printf_type e_printf ;
  842.  
  843.  /* Global error list array. */
  844.  
  845.  err_type   err_list[MAXERRORS] =
  846.     {{ 1  , 0,"Error 1. Unexpected end of line." ,NULL,NULL },
  847.      { 2  , 0,"Error 2. Unexpected symbol.",NULL,NULL },
  848.      { 3  , 0,"Error 3. Unexpected token char.",NULL,NULL },
  849.      { 4  , 0,"Error 4. Symbol/Literal contains invalid char.",NULL,NULL },
  850.      { 10 , 0,"Error 10. Command op size invalid.",NULL,NULL },
  851.      { 11 , 0,"Error 11. Invalid address mode.",NULL,NULL },
  852.      { 12 , 0,"Error 12. Unrecognized command.",NULL,NULL },
  853.      { 13 , 0,"Error 13. Command operands required.",NULL,NULL},
  854.      { 14 , 0,"Error 14. Forward references not allowed here.",NULL,NULL},
  855.      { 15 , 0,"Error 15. Too many operands.",NULL,NULL},
  856.      { 16 , 0,"Error 16. Line label required.",NULL,NULL},
  857.      { 17 , 0,"Error 17. Label found in external list.",NULL,NULL},
  858.      { 18 , 0,"Error 18. Label already resolved.",NULL,NULL},
  859.      { 19 , 0,"Error 19. Operand symbol already resolved.",NULL,NULL},
  860.      { 21 , 0,"Error 21. Address collision.",NULL,NULL},
  861.      { 22 , 0,"Error 22. Location counter is odd.",NULL,NULL},
  862.      { 23 , 0,"Error 23. Unresolved symbol.",NULL,NULL},
  863.      { 30 , 0,"Error 30. File not found or unable to open.",NULL,NULL },
  864.      { 31 , 0,"Error 31. Unexpected end of file.",NULL,NULL },
  865.      { 33 , 0,"Error 33. Disk full while writing file.",NULL,NULL},
  866.      { 41 , 0,"Error 41. Out of Dynamic memory.",NULL,NULL },
  867.      { 50 , 0,"Warning 50. Symbol too long. Truncated.",NULL,NULL},
  868.      { 52 , 0,"Warning 52. Value out of range.",NULL,NULL},
  869.      { 70 , 0,"Warning 70. Expression/Syntax imprecise.",NULL,NULL},
  870.      { 71 , 0,"Warning 71. Command op size not specified.",NULL,NULL},
  871.      { 72 , 0,"Warning 72. Line label not allowed. Ignored.",NULL,NULL},
  872.      { 73 , 0,"Warning 73. Command operands not allowed. Ignored.",NULL,NULL},
  873.      { 74 , 0,"Warning 74. Symbol already in global list. Ignored.",NULL,NULL},
  874.      { 75 , 0,"Warning 75. Symbol already in external list. Ignored.",NULL,NULL},
  875.      { 100, 0,"FSE 100. Invalid return from next_token.",NULL,NULL} };
  876.  
  877.  
  878.  
  879. /***************************************************************************
  880.  Function e_message
  881.       This function looks up the code in the errlist array, retrieves
  882.       the standard message from the array, and inserts the message into
  883.       the current errptr list .  It puts the additional message
  884.       in the add_message field.
  885.  
  886.    Error code map :
  887.      0..49  errors.
  888.      50..99 warnings
  889.      100..  fatal software errors.
  890.  
  891.  **************************************************************************/
  892.  void  e_message( char     pos     ,
  893.           char     code     ,
  894.           char * add_mess)
  895.  {
  896.   err_type   * temp ;
  897.   int           i    ;
  898.  
  899.   char * tmp;
  900.  
  901.   temp = NULL ;
  902.   if ( e_hold_messages ) {
  903.      temp = ( err_type * ) malloc ( sizeof(err_type) ) ;
  904.      if ( !temp ) code = 41 ;  /* change code to that of 'out of memory' */
  905.      }
  906.  
  907.   i = 0 ;
  908.   while (( i < MAXERRORS ) && ( err_list[i].code != code )) i ++ ;
  909.  
  910.   if ( code == 41 )
  911.      e_error.out_of_memory = TRUE ;
  912.  
  913.   if ( temp ) {
  914.      temp->message = ( i <= MAXERRORS ) ? err_list[i].message : NULL ;
  915.      if ( *add_mess )
  916.     temp->add_mess = ( char * ) strdup ( add_mess ) ;
  917.      else
  918.     temp->add_mess = NULL ;
  919.  
  920.      temp->code    = code ;
  921.      temp->position= pos ;
  922.      temp->next = NULL ;
  923.  
  924.      if ( !e_error.errptr ) {
  925.     e_error.errptr = temp ;
  926.     e_error.last   = temp ;
  927.     }
  928.      else {
  929.        e_error.last->next = temp ;
  930.        e_error.last = temp ;
  931.        }
  932.      }
  933.  
  934.   tmp = ( char * ) ( i < MAXERRORS ) ? err_list[i].message : NULL ;
  935.  
  936.   if ( e_error.curpos )
  937.     pos += e_error.curpos ;
  938.  
  939.   e_printf( tmp, add_mess );
  940.  
  941.   if ( ( code < 50 ) || ( code >= 100 ) ) {
  942.      e_error.errors++ ;
  943.      e_error.state++ ;
  944.      }
  945.   else
  946.      e_error.warnings++;
  947.  
  948.  } /* e_message */
  949.  
  950.  
  951.  
  952. /***************************************************************************
  953.  Procedure e_delete_errors
  954.       This procedure deletes all the errors currently attached to
  955.       e_error.errptr .
  956.  
  957.  **************************************************************************/
  958.  void e_delete_errors()
  959.  {
  960.   err_type  * temp ;
  961.  
  962.   while ( e_error.errptr ) {
  963.      temp = e_error.errptr->next ;
  964.      if ( e_error.errptr->add_mess )
  965.     free( e_error.errptr->add_mess ) ;
  966.      free( e_error.errptr );
  967.      e_error.errptr = temp ;
  968.      }
  969.  } /* e_delete_errors */
  970.  
  971.  
  972. [LISTING FOUR]
  973.  
  974.  
  975. /*
  976.      68000 Instruction Set Module.
  977.      This module contains those procedures needed to handle
  978.      the instruction set.
  979. */
  980.  
  981.   #include <stdio.h>
  982.   #include <stdlib.h>
  983.   #include <string.h>
  984.  
  985.   #include "68defs.h"
  986.   #include "68err.h"
  987.   #include "68parse.h"
  988.   #include "68list.h"
  989.   #include "68assem.h"
  990.   #include "68instr.h"
  991.  
  992.   #define LOCAL near pascal
  993.  
  994.  
  995.   /**************************************************************************
  996.     is_validate
  997.       This procedure validates an instruction. It first looks up the
  998.       instruction mnemonic in the is_array, then determines if the
  999.       source and destination are valid for the particular instruction.
  1000.  
  1001.       Input parameters :
  1002.       size      : size of the operation. i.e    ADD.W
  1003.       command : the instruction mnemonic string.
  1004.       Variable parameters
  1005.       opcode  : the base operation code determined
  1006.  
  1007.       termlist : updates sizeofreserve field
  1008.  
  1009.       index   : the instruction array index for the instruction
  1010.       Return code :
  1011.       0        : instruction addr mode is valid.
  1012.       otherwise : error occured. Returned in error list.
  1013.    **************************************************************************/
  1014.  
  1015.   int    is_validate ( p_size_type *  size   ,
  1016.               char      *  command,
  1017.               char         smode  ,
  1018.               char         sreg   ,
  1019.               char         dmode  ,
  1020.               char         dreg   ,
  1021.               unsigned int * opcode ,
  1022.               char       * total_size ,
  1023.               am_term_type * termlist ,
  1024.               unsigned int * index    )
  1025.  
  1026.   {
  1027.  
  1028.     /* error if destination is not specified */
  1029.     if ( (dmode==7) && ( dreg==5 ) ) {
  1030.     e_message(0,11," Destination operand required.");
  1031.     return 11 ;
  1032.     }
  1033.  
  1034.     *total_size = 1 ;
  1035.     /* lookup command */
  1036.     if (  ! strcmp(command,"MOVE")  ) {
  1037.        /* error if source is not specified */
  1038.        if ( (smode==7) && ( sreg==5 ) ) {
  1039.       e_message(0,11," Source operand required.");
  1040.       return 11 ;
  1041.       }
  1042.        if ( *size == p_unknown ) {
  1043.       /* assign default size if it's unknown */
  1044.       e_message(0,71," Assumed Long.");
  1045.       *size = p_long ;
  1046.       }
  1047.  
  1048.        termlist->sizeofreserve=am_abs_address_size ;
  1049.        termlist->next->sizeofreserve=am_abs_address_size ;
  1050.        *total_size += ( am_abs_address_size << 1 );
  1051.  
  1052.        *opcode =  (dreg << 9 ) | ( dmode <<6 ) | ( smode <<3 ) | sreg ;
  1053.        *opcode |= ( *size == p_long )? 0x2000: (*size==p_word)? 0x3000:0x1000;
  1054.        *index = 0;
  1055.        }
  1056.     else if (  ! strcmp(command,"JMP")  ) {
  1057.        *opcode =  0x4EC0 | ( dmode << 3 ) | dreg ;
  1058.        termlist->sizeofreserve=am_abs_address_size ;
  1059.        *total_size += am_abs_address_size ;
  1060.        *index = 1 ;
  1061.        }
  1062.     else {
  1063.        e_message(0,12,command);
  1064.        return 12 ;
  1065.        }
  1066.  
  1067.     return 0 ;
  1068.  
  1069.   } /* is_validate */
  1070.  
  1071.  
  1072.  
  1073. [LISTING FIVE]
  1074.  
  1075.  
  1076. /*
  1077.     68000 listing module.
  1078. */
  1079.  
  1080. #include <stdio.h>
  1081. #include <stdlib.h>
  1082. #include <string.h>
  1083. #include <time.h>
  1084. #include <ctype.h>
  1085.  
  1086. #include "68defs.h"
  1087. #include "68err.h"
  1088. #include "68list.h"
  1089.  
  1090.  
  1091.  /* These are considered module level variables. */
  1092.  l_line_type *    l_line_head = NULL ;
  1093.  char         *    l_header    =
  1094.   " Generic Assembler Version 1.0 :                            ";
  1095.  char         *    l_blanks    = "                        " ;
  1096.  int        l_number_of_lines = 0 ;
  1097.  
  1098. /****************************************************************************
  1099.   Function l_addline
  1100.      This funciton adds a line at the end of the line listing.
  1101.      It does this according to the class of the line :
  1102.     l_firstinstr : It creates a node for the text of the
  1103.                line to be held in.  It concats a blank
  1104.                leader before the text to hold the address
  1105.                and opcode. If there are more than two words
  1106.                in the instruction, then an additional
  1107.                line node in created.
  1108.     l_data         : It creates a node,and concats a blank leader onto
  1109.                the text line.
  1110.  
  1111.     Form of instruction line :
  1112.          blanks        line text
  1113.     addres |        |
  1114. first-> 000000     0000 0000 0000 Lable    command  operand text
  1115.     0--------------------23
  1116. other->      0000 0000
  1117.     0--------------------23
  1118.   NOTE :
  1119.      This routine copys the line text, so that the caller may reuse the
  1120.      line, or discard it with out affecting the listing.
  1121.  
  1122.   Input parameters :
  1123.      class    : the class of line to be added.
  1124.      numofwords : number of words in the instrcuction if its an
  1125.           instruction line.
  1126.      line    : the text of the line to be placed in the listing.
  1127.  
  1128.   Variable parameters :
  1129.      lineptr    : pointer to the line which was added to the listing.
  1130.  
  1131.   Return
  1132.      0    : line added okay.
  1133.      41 : out of memory
  1134.  
  1135.  **************************************************************************/
  1136.  int  l_addline( l_lclass_type      class      ,
  1137.          char          numofwords ,
  1138.          char        * line         ,
  1139.          l_line_type   ** lineptr    )
  1140. {
  1141.  l_line_type  * curline , * tcurline ;
  1142.  register int    i ;
  1143.  long        secs ;
  1144.  
  1145.  *lineptr = NULL ;
  1146.  curline = NULL ;
  1147.  curline = ( l_line_type * ) malloc ( sizeof(l_line_type) ) ;
  1148.  
  1149.  if ( !curline ) {
  1150.     e_message(0,41," Listing " ) ; /* out of memory */
  1151.     return 41 ;
  1152.     }
  1153.  
  1154.  curline->lclass = class ;
  1155.  curline->linenum = ( class != l_firstinstr ) ? 0 :l_number_of_lines++ ;
  1156.  
  1157.  if ( !l_line_head ) {
  1158.     l_line_head = ( l_line_type * ) malloc ( sizeof(l_line_type));
  1159.     if (!l_line_head){
  1160.       e_message(0,41 ," Listing " ) ; /* out of memory */
  1161.       return 41 ;
  1162.       }
  1163.     l_line_head->line = l_header ;
  1164.     time(&secs);
  1165.     strcpy ( ((l_line_head->line)+32), asctime( localtime(&secs) ) );
  1166.     i = strlen(l_line_head->line);
  1167.     for(;!isprint(l_line_head->line[i]);i--) l_line_head->line[i]=0;
  1168.     l_line_head->lclass = l_data ;
  1169.     l_line_head->linenum = 0 ;
  1170.     l_line_head->next = l_line_head ;
  1171.     l_line_head->prev = l_line_head ;
  1172.     }
  1173.  
  1174.  curline->line = ( char * ) malloc(  25 + strlen(line) );
  1175.  if ( !(curline->line) ) {
  1176.     e_message(0,41 ," Listing " ) ; /* out of memory */
  1177.     free(curline);
  1178.     return 41 ;
  1179.     }
  1180.  
  1181.  curline->next = l_line_head ;
  1182.  curline->prev = l_line_head->prev ;
  1183.  l_line_head->prev = curline ;
  1184.  curline->prev->next = curline;
  1185.  
  1186.  
  1187.  if ( class != l_neither ) {
  1188.     memset( (curline->line) ,'0', 6 );
  1189.     memset( ((curline->line)+6),' ',18 );
  1190.     }
  1191.  else
  1192.     memset( (curline->line) ,' ',24);
  1193.  
  1194.  strcpy( ((curline->line)+24) ,line);
  1195.  
  1196.  tcurline = curline ;
  1197.  
  1198.  if ( numofwords > 3 ) {
  1199.     /* allocate post lines for either instr or data */
  1200.     i = (numofwords - 3) / 3 ;
  1201.     if ( (numofwords - 3) % 3  ) i++ ;
  1202.     for ( ; i ; i-- ) {
  1203.        curline = ( l_line_type * ) malloc ( sizeof(l_line_type) ) ;
  1204.        if ( !curline ) {
  1205.       e_message(0,41 , NULL ) ; /* out of memory */
  1206.       return 41 ;
  1207.       }
  1208.        curline->next = l_line_head ;
  1209.        curline->prev = l_line_head->prev ;
  1210.        l_line_head->prev = curline ;
  1211.        curline->prev->next = curline;
  1212.  
  1213.        curline->line = strdup( l_blanks );
  1214.        if ( !(curline->line) ) {
  1215.       e_message(0,41 , NULL ) ; /* out of memory */
  1216.       return 41 ;
  1217.       }
  1218.  
  1219.        curline->lclass = ( class == l_firstinstr ) ? l_otherinstr:l_data ;
  1220.        curline->linenum =( class == l_firstinstr ) ? l_number_of_lines++ : 0 ;
  1221.  
  1222.        }
  1223.  
  1224.     }
  1225.  
  1226.  *lineptr = tcurline ;
  1227.  return 0 ;
  1228.  
  1229. } /* l_addline */
  1230.  
  1231.  
  1232.  
  1233. /****************************************************************************
  1234.   Function l_writetoline
  1235.      This funciton writes to a line in the listing, assuming it's
  1236.      already been created.
  1237.      It does this by refering to specific elements in the address opcode
  1238.      field by identifiers such as :
  1239.        0 : write 6 hex degit address field
  1240.        1 : write the 4 hex degit opcode/data field  for word 1 .
  1241.        2 : write the 4 hex degit opcode/data field  for word 2 .
  1242.        etc..
  1243.      It takes the data to be written ( whether address/data/opcode ) from
  1244.      the unsigned long parameter called :
  1245.     VALUE
  1246.      The option specifies special operations as follows :
  1247.        0 : write only one number as specified by spec .
  1248.        1 : write question marks in field for only one field specified by spec.
  1249.        2 : write repeated number over all post words up to the
  1250.        field specified by spec. ( spec 0 is ignored. )
  1251.        3 : write repeated question marks over all post words up to the
  1252.        field specified by spec. ( spec 0 is ignored. )
  1253.        4 : write only one byte at location specified by spec.
  1254.  
  1255.   Input parameters :
  1256.      spec    : the specification number as described above.
  1257.      value    : the value to be written on the line.
  1258.      option    : flag indicated if question marks are to be printed.
  1259.      line    : pointer to the listing node containing the first line.
  1260.  
  1261.  **************************************************************************/
  1262.  void    l_writetoline( int        spec       ,
  1263.                unsigned long    value       ,
  1264.                char        option       ,
  1265.                l_line_type    * line       )
  1266. {
  1267.  register int  i       ;
  1268.  char         * curline ;
  1269.  
  1270.  
  1271.  if ( !line ) return ;
  1272.  
  1273.  if ( !spec ) {
  1274.     if ( option )
  1275.        strnset(line->line,'?',6);
  1276.     else {
  1277.        sprintf(line->line,"%06X",value);
  1278.        *(line->line+6) = ' ' ;
  1279.        }
  1280.     }
  1281.  else {
  1282.     i = ( spec - 1 ) / 3 ;
  1283.     for (  ; i ; i-- , line = line->next ) {
  1284.     if ( option == 2 ) {
  1285.        sprintf(line->line+6,"   %04X %04X %04X",( int ) value,
  1286.            ( int ) value, ( int ) value );
  1287.        *(line->line+23) = ' ';
  1288.        }
  1289.     else if ( option == 3 )  {
  1290.        sprintf(line->line+6,"   ???? ???? ????" );
  1291.        *(line->line+23) = ' ';
  1292.        }
  1293.     }
  1294.  
  1295.     i = ( spec - 1 ) % 3  ;
  1296.     curline = line->line + 9 ;
  1297.     for ( ; i ; i-- , curline += 5 ) {
  1298.     if ( option == 2 )  {
  1299.        sprintf( curline,"%04X",( unsigned int ) value);
  1300.        *(curline+4) = ' ';
  1301.        }
  1302.     else if ( option == 3 )
  1303.        strnset( curline,'?',4 );
  1304.     }
  1305.     if ( ( option == 0 ) || ( option == 2 ) )  {
  1306.        sprintf(curline,"%04X", ( unsigned int ) value ) ;
  1307.        *(curline+4)=' ';
  1308.        }
  1309.     else if ( option == 4 ) {
  1310.        sprintf(curline,"%02X", ( unsigned char ) value );
  1311.        *(curline+2)=' ';
  1312.        }
  1313.     else
  1314.        strnset(curline,'?',4);
  1315.     }
  1316.  
  1317. } /* l_writetoline */
  1318.  
  1319.  
  1320. /****************************************************************************
  1321.   Function l_add_errors
  1322.      This funciton writes all the error messages attached to e_error.errptr
  1323.      to the listing, then deletes the error list.
  1324.   Input
  1325.      lptr  : pointer to the line in the listing after which the
  1326.          errors should be attached.
  1327.  
  1328.  **************************************************************************/
  1329.  void    l_add_errors ( l_line_type * lptr )
  1330.  {
  1331.   l_line_type    * tlptr ;
  1332.   err_type    * error ;
  1333.   char          relink_list ;
  1334.   char          message_buf[100] ;
  1335.  
  1336.   if ( !lptr )    /* if lptr == NULL */
  1337.       lptr = l_line_head->prev ;
  1338.  
  1339.   /* if lptr at end of list do not bother relinking listing.*/
  1340.   relink_list = ( lptr->next != l_line_head ) ;
  1341.  
  1342.   for ( error = e_error.errptr ; error ; error = error->next ) {
  1343.      strcpy( message_buf , error->message ) ;
  1344.      strcpy( message_buf+strlen(error->message), error->add_mess );
  1345.      l_addline(l_neither, 0, message_buf , &tlptr );
  1346.      if ( relink_list ) {
  1347.     /* unlink the  line */
  1348.     tlptr->next->prev = tlptr->prev ;
  1349.     tlptr->prev->next = tlptr->next ;
  1350.  
  1351.     /* link the line into listing after the source line at lptr */
  1352.     tlptr->next = lptr->next ;
  1353.     tlptr->next->prev = tlptr ;
  1354.     tlptr->prev = lptr ;
  1355.     lptr->next = tlptr ;
  1356.     }
  1357.      }
  1358.   e_delete_errors() ;
  1359.  
  1360.  } /* l_add_errors */
  1361.  
  1362.  
  1363. /****************************************************************************
  1364.   Function l_printlisting
  1365.      This funciton writes all the listing lines into the text file
  1366.      passed in as outfile. It assumes the file was already opened
  1367.      for text output. It will stop writing if an error occurs
  1368.      while writing.
  1369.   Input
  1370.      outfile : the already opened file that the listing is written to.
  1371.      withheader : TRUE if list header is to be written first.
  1372.           FALSE if list header not to be written at all.
  1373.   Return
  1374.      0 : okay.
  1375.      33: error while writing to file.
  1376.  
  1377.  **************************************************************************/
  1378. int  l_printlisting( FILE * outfile, char withheader )
  1379. {
  1380.  l_line_type * curline ;
  1381.  
  1382.  if ( withheader )
  1383.     fprintf(outfile,"%s\n",l_line_head->line);
  1384.  
  1385.  for ( curline = l_line_head->next ; curline != l_line_head ;
  1386.        curline = curline->next ) {
  1387.       if ( (curline->lclass != l_neither)&&(curline->lclass != l_data) ) {
  1388.     if ( fprintf(outfile,"%3d %s\n",curline->linenum,
  1389.              curline->line) == EOF ) {
  1390.        e_message(0,33,NULL);
  1391.        return 33 ;
  1392.        }
  1393.     }
  1394.       else if ( fprintf(outfile,"    %s\n", curline->line) == EOF ) {
  1395.      e_message(0,33,NULL);
  1396.      return 33 ;
  1397.      }
  1398.       }
  1399.  return 0 ;
  1400.  
  1401. } /* l_printlisting */
  1402.  
  1403.  
  1404. /****************************************************************************
  1405.   Function l_delete_listing
  1406.      This funciton deletes the entire listing, and resets all of
  1407.      its associated pointers back to their default values.
  1408.   Input
  1409.      deletehead : TRUE if head is to be deleted too.
  1410.           FALSE if head to be left alone.
  1411.      resetcount : TRUE if l_number_of_lines should be reset.
  1412.  
  1413.  
  1414.  **************************************************************************/
  1415. void l_delete_listing( char deletehead , char resetcount )
  1416. {
  1417.  l_line_type * curline, * tline ;
  1418.  
  1419.  if ( resetcount ) l_number_of_lines = 0 ;
  1420.  
  1421.  if ( l_line_head )  {
  1422.     l_line_head->prev->next = NULL ;
  1423.     curline = l_line_head->next ;
  1424.  
  1425.     l_line_head->next = l_line_head ;
  1426.     l_line_head->prev = l_line_head ;
  1427.     if ( deletehead ) {
  1428.        free(l_line_head);
  1429.        l_line_head = NULL ;
  1430.        }
  1431.     while ( curline ) {
  1432.       tline = curline->next ;
  1433.       if ( curline->line ) free( curline->line );
  1434.       free( curline );
  1435.       curline=tline ;
  1436.       }
  1437.     }
  1438.  
  1439. } /* l_delete_listing */
  1440.  
  1441.  
  1442. [LISTING SIX]
  1443.  
  1444.  
  1445. /*
  1446.     68000 math module.
  1447. */
  1448.  
  1449. #include <stdio.h>
  1450. #include <string.h>
  1451. #include <ctype.h>
  1452.  
  1453. #include "68defs.h"
  1454. #include "68err.h"
  1455. #include "68parse.h"
  1456.  
  1457.  
  1458. /****************************************************************************
  1459.   Function strtolong
  1460.      This funciton converts a string of base to a long integer.
  1461.  
  1462.      It behaves the way that STRTOL is supposed to.
  1463.  
  1464.      Example
  1465.        num = strtolong("101", &endptr, 2);
  1466.        gives num = 5 ;
  1467.  
  1468.   Input parameters :
  1469.      str  : the string of valid numberic ascii characters
  1470.      endptr : if no error points to end of string .
  1471.           if error it points to error location in string.
  1472.      base : the base of the string : either 2, 8, 16, or 10
  1473.  **************************************************************************/
  1474.  unsigned long int strtolong ( register char    *  str      ,
  1475.                    char        ** endptr ,
  1476.                    char           base   )
  1477.  
  1478. {
  1479.    unsigned long int  sum  = 0L ;
  1480.    register char shift ;
  1481.  
  1482.    switch ( base ) {
  1483.      case 10 : for (; *str ; str++)
  1484.          if ( isdigit(*str) )
  1485.             sum = ( sum * 10 ) + ( *str - '0' ) ;
  1486.          else {
  1487.             *endptr = str ;
  1488.             return 0 ;
  1489.             }
  1490.            *endptr = str ;
  1491.            return sum ;
  1492.      case 2  : shift = 1 ; break ;
  1493.      case 8  : shift = 3 ; break ;
  1494.      case 16 : shift = 4 ; break ;
  1495.      }
  1496.  
  1497.    for (; *str ; str++ )
  1498.       if ( isdigit ( *str ) &&
  1499.       (  ( base == 10 ) || ( base == 16 ) ||
  1500.          ( (base == 2) && ( *str == '0' || *str == '1' ) ) ||
  1501.          ( (base == 8) && ( *str <= '7' && *str >= '0' ) ) ) )
  1502.      sum  = (  sum << shift  ) | ( *str - '0' ) ;
  1503.       else if ( isxdigit (*str) && ( base == 16 ) )
  1504.      sum  = ( sum << shift ) | ( toupper(*str) - 'A' + 10 ) ;
  1505.       else {
  1506.      *endptr = str ;
  1507.      return 0 ;
  1508.      }
  1509.  
  1510.    *endptr = str ;
  1511.    return sum ;
  1512.  
  1513. } /* strtolong */
  1514.  
  1515.  
  1516.  
  1517. /***************************************************************************
  1518.  Function m_symtoval
  1519.  
  1520.     This function converts a valid numerical symbol string to its
  1521.     corresponding long value. It follows the format below :
  1522.     All symbols which start with an 0..9,%,@,$," are numerical symbols.
  1523.     All others are ordinary symbols.
  1524.  
  1525.     Numeric
  1526.         $ddd  -Hex     %ddd - Binary      @ddd - Octal     ddd - Decimal
  1527.          daaH      dddB           dddO      dddD
  1528.                        dddQ
  1529.  
  1530.         '4444' - Quoted literal of max 4 Ascii characters.
  1531.  
  1532.  
  1533.      0    : a valid number is returned
  1534.      4    : an invalid char was found in symbol
  1535.      otherwise its not a valid number, although it may be a valid symbol.
  1536.  
  1537.  ***************************************************************************/
  1538. int     m_symtoval( char * sym , unsigned long int  * value )
  1539.  
  1540. {
  1541.   char      symbol[MAXSYMLEN] ;
  1542.   char      ch , * last , base ;
  1543.  
  1544.  
  1545.   strncpy( symbol, sym, MAXSYMLEN );  /* make a local pass by value symbol*/
  1546.   symbol[MAXSYMLEN-1] = 0 ;
  1547.  
  1548.   last = symbol+strlen(symbol)-1 ;
  1549.   *value = 0L ;
  1550.  
  1551.   if ( isdigit( ch=*symbol ) )
  1552.      switch ( toupper( *last )    ) {
  1553.        case 'H' : *last = 0 ; base = 16 ; break ;
  1554.        case 'B' : *last = 0 ; base = 2  ; break ;
  1555.        case 'Q' :
  1556.        case 'O' : *last = 0 ; base = 8  ; break ;
  1557.        case 'D' : *last = 0 ;
  1558.        default    :  base = 10 ; break ;
  1559.        }
  1560.   else {
  1561.      switch ( ch ) {
  1562.     case '$' : symbol[0] = '0' ; base = 16 ; break ;
  1563.     case '@' : symbol[0] = '0' ; base = 8 ; break ;
  1564.     case '%' : symbol[0] = '0' ; base = 2 ; break ;
  1565.     case '\'':
  1566.            /* scan line until next ' */
  1567.            last = strchr( symbol+1, '\'');
  1568.            if ( !(*last) ) {
  1569.               e_message(0,51,"End quote expected.");
  1570.               last = symbol+5 ;
  1571.               }
  1572.            *last = 0 ;
  1573.            if ( last - symbol  > 5 ) {
  1574.                e_message(0,51,NULL);
  1575.                symbol[5] = 0 ;
  1576.                }
  1577.            for ( last = symbol+1 ; *last ; last++ )
  1578.                *value = ( *value << 8 ) | *last ;
  1579.            return 0 ;
  1580.     default  : return 1 ;
  1581.     }
  1582.      }
  1583.  
  1584.      *value = ( unsigned long int ) strtolong( symbol, &last , base );
  1585.      if ( *last ) e_message(0,4,NULL);
  1586.      return  ( *last  ) ? 4 : 0 ;
  1587.  
  1588. } /* m_symtoval */
  1589.  
  1590.  
  1591.  
  1592.  
  1593. [LISTING SEVEN]
  1594.  
  1595.  
  1596. /*
  1597.     68000 parser module.
  1598. */
  1599.  
  1600.  
  1601. #include <stdio.h>
  1602. #include <alloc.h>
  1603. #include <string.h>
  1604. #include <ctype.h>
  1605.  
  1606. #include "68defs.h"
  1607. #include "68err.h"
  1608. #include "68parse.h"
  1609. #include "68list.h"
  1610. #include "68assem.h"
  1611. #include "68math.h"
  1612.  
  1613. #define LOCAL near pascal
  1614.  
  1615.    typedef enum { symbol, token, none } p_tok_type ;
  1616.  
  1617.    typedef enum {  pn_sym   ,         /* sym */
  1618.            pn_empty ,         /* empty parse node. */
  1619.            pn_error         /* error */
  1620.         } p_addr_type ;
  1621.  
  1622.    typedef struct {
  1623.            p_addr_type     p_nodeclass;/* tag field */
  1624.            char      regnum   ;
  1625.            p_sym_type *  symptr  ; /* symbol list, if any. */
  1626.           } p_node_type ;
  1627.  
  1628.  
  1629.  
  1630.     char      symchars[38] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_" ;
  1631.  
  1632.     p_sym_type      curtoksym ;       /* current token symbol from next_token */
  1633.     char      curtok ;       /* current token character from next_token*/
  1634.  
  1635.  
  1636.  /* Function prototypes for local functions */
  1637.  
  1638.  static p_tok_type LOCAL next_token( char * line     , int  * line_offset  ,
  1639.                      int    line_len );
  1640.  
  1641.  static int LOCAL  p_symbol ( char *      line       , int  * line_offset   ,
  1642.                   int      line_len , char   symknown      ,
  1643.                   char      preop    , p_sym_type ** symptr ,
  1644.                   p_node_type * p_node   );
  1645.  
  1646.  static p_size_type LOCAL p_dotsize ( char * line    ,
  1647.                       int  * line_offset ,
  1648.                       int    line_len ,
  1649.                       int    prev_offset );
  1650.  static void LOCAL p_parse_line ( char *   line     , int  * line_offset    ,
  1651.                   int       line_len , p_node_type *  p_node ,
  1652.                   char *   done     );
  1653.  
  1654.  
  1655.  
  1656.  
  1657. /***************************************************************************
  1658.  
  1659.  Function next_token  - get next token
  1660.  
  1661.     This function returns the next token in the text line from
  1662.     the current line offset.  If rest of line is blank, or if
  1663.     line offset is at end of line, then it returns an end of line
  1664.     indicator.
  1665.  
  1666.     Input parameters :
  1667.        line        - the line to be parsed.
  1668.        line_offset  - the current offset into the line.
  1669.        line_len     - the line length. ( so as to avoid calling strlen )
  1670.  
  1671.     Calls :
  1672.       p_symtype : to reclassify symbols .
  1673.  
  1674.     Warnings : e_message called as warnings occur.
  1675.       50 : Symbol too long
  1676.  
  1677.     Globals :
  1678.       curtoksym
  1679.       curtok
  1680.  
  1681.     Return value :
  1682.       symbol : symbol found.  Is located in curtoksym.
  1683.       token  : token found.   Token character is in curtok.
  1684.       none   : end of line found.
  1685.  
  1686.  ****************************************************************************/
  1687.   static p_tok_type LOCAL next_token( char * line     , int  * line_offset  ,
  1688.                       int    line_len )
  1689.  
  1690. {
  1691.   char           * tokpos, * tline ;
  1692.   int           symlen  ;
  1693.  
  1694.  
  1695.   (*line_offset) += strspn( line + *line_offset , " \t");  /* skip blanks */
  1696.  
  1697.   if ( *line_offset >= line_len )
  1698.      return none ;
  1699.  
  1700.   tline = line + *line_offset ;
  1701.  
  1702.   if ( *tline == '\'') {
  1703.      tokpos = strchr( tline+1, '\'');      /* scan for next quote.*/
  1704.      if ( *tokpos )               /* if found set tok one further. */
  1705.     tokpos++ ;               /* if not , set to shortest of   */
  1706.      else                   /* (5 forward or the end)        */
  1707.     tokpos = ( tokpos - tline > 5 ) ? tline + 5 : tokpos ;
  1708.      }
  1709.   else
  1710.     tokpos = strpbrk( tline, ".;-+()#/, \"\t");
  1711.  
  1712.  
  1713.   if ( tokpos != tline ) {
  1714.      /* return symbol between start of line and tokpos. */
  1715.      if ( !(*tokpos) ) tokpos = line + line_len ;  /* correct for NULL */
  1716.      symlen = tokpos - tline ;
  1717.      (*line_offset) += symlen;
  1718.      if ( symlen >= MAXSYMLEN ) e_message(*line_offset,50,NULL);
  1719.      symlen = ( symlen >= MAXSYMLEN ) ? MAXSYMLEN - 1 : symlen ;
  1720.      strncpy( curtoksym.sym, tline, symlen ) ;
  1721.      curtoksym.sym[symlen] = 0 ;
  1722.  
  1723.      if ( isalpha(curtoksym.sym[0] ) )
  1724.     if ( strspn( curtoksym.sym, symchars ) == symlen )
  1725.        return symbol ;
  1726.     else {
  1727.        e_message(*line_offset,4,NULL);
  1728.        return symbol ;
  1729.        }
  1730.  
  1731.      e_error.curpos = *line_offset ;
  1732.      if ( ( !m_symtoval(curtoksym.sym,&curtoksym.val ) ) &&
  1733.       ( !e_error.state ) )
  1734.       curtoksym.sym[0] = 0 ;
  1735.      e_error.curpos = 0 ;
  1736.  
  1737.      return  symbol ;
  1738.      }
  1739.   else
  1740.      {
  1741.      /*  the token is at the start of the line. */
  1742.      curtok = *tline ;
  1743.      (* line_offset ) ++ ;
  1744.      return token ;
  1745.      }
  1746.  
  1747. } /* next_token */
  1748.  
  1749.  
  1750.  
  1751. /***************************************************************************
  1752.  
  1753.  Function p_symbol  - parse symbol
  1754.  
  1755.     This function parses a symbol into its components following the
  1756.     diagram :
  1757.  
  1758.        symbol --> operator --->
  1759.       ^            |
  1760.       |-----------------|
  1761.  
  1762.        symbol     - any valid non-key word of SYMMAXLEN length or less.
  1763.        operator  -  plus  '+'
  1764.             minus '-'
  1765.  
  1766.        If an unrecognized operator is encountered, the symbol chain
  1767.       is assumed to have come to an end.  The last token is then
  1768.       un-read, so that it can be re-read by subsequent parse code.
  1769.  
  1770.     Input parameters :
  1771.        line        - the line to be parsed.
  1772.        line_offset  - the current offset into the line.
  1773.        line_len     - the line length. ( so as to avoid calling strlen )
  1774.        symknown     - flag ( TRUE or FALSE ) which indicates whether
  1775.               the initial symbol was already parsed.
  1776.  
  1777.     Note : calls e_message to print errors as it recurses up.
  1778.  
  1779.     Warnings : e_message called as warnings occur.
  1780.       51 : literal too long
  1781.  
  1782.     Return code :
  1783.       0 : okay
  1784.       1 : Unexpected eoln
  1785.       2 : Unexpected symbol.
  1786.       3 : Unexpected token char.
  1787.       41  : Out of dynamic memory.
  1788.       100 : Invalid return from next token.
  1789.  ****************************************************************************/
  1790.  static int LOCAL  p_symbol ( char *      line       , int  * line_offset   ,
  1791.                   int      line_len , char   symknown      ,
  1792.                   char      preop    , p_sym_type ** symptr ,
  1793.                   p_node_type * p_node   )
  1794. {
  1795.   p_tok_type   tok ;
  1796.   int           i ;
  1797.  
  1798.  
  1799.   if ( symknown )
  1800.      tok = symbol ;
  1801.   else
  1802.      tok = next_token( line , line_offset , line_len) ;
  1803.  
  1804.   switch ( tok ) {
  1805.      case symbol : /* Symbol was found */
  1806.            *symptr = ( p_sym_type * ) malloc ( sizeof(p_sym_type));
  1807.            if ( ! (*symptr) ) {   /* if *symptr == NULL */
  1808.               e_message(0,41," Parser ");
  1809.               return 41 ;
  1810.               }
  1811.  
  1812.            memcpy( (*symptr) , &curtoksym, sizeof(p_sym_type) );
  1813.            (*symptr)->operator = preop ;
  1814.            (*symptr)->next = NULL ;
  1815.  
  1816.            i = *line_offset ; /* hold onto line offset */
  1817.  
  1818.            tok = next_token( line , line_offset , line_len);
  1819.            switch ( tok ) {
  1820.              case token :
  1821.               switch ( curtok ) {
  1822.                  case '+' :
  1823.                  case '-' :
  1824.                  i = p_symbol( line, line_offset,
  1825.                        line_len, FALSE, curtok,
  1826.                        &( (*symptr)->next ), p_node);
  1827.                  return i ;
  1828.                  default  :
  1829.                  /* un-get the token. */
  1830.                  *line_offset = i ;
  1831.                  return 0;
  1832.                  }
  1833.              case symbol : e_message(*line_offset,2,NULL);
  1834.                    return 2 ;
  1835.              case none     : return 0 ;
  1836.              default     : e_message(*line_offset,100,"p_symbol");
  1837.                    return 100 ;
  1838.              }
  1839.      case token  : e_message(*line_offset,3,NULL);
  1840.            return 3 ;
  1841.      case none     : e_message(*line_offset,1,NULL);
  1842.            return 1 ;
  1843.      default     : e_message(*line_offset,100,"p_symbol");
  1844.            return 100 ;
  1845.      }
  1846.  
  1847. } /* p_symbol */
  1848.  
  1849.  
  1850.  
  1851. /*****************************************************************************
  1852.  Function p_dotsize
  1853.     This function parses a dot size field and returns the size as a type.
  1854.     It expects that the current token is a token/delemeter and not a symbol
  1855.  
  1856.     If the curtok is not a '.' it unreads it and returns p_unknown.
  1857.     If the curtok is a '.' it follows :
  1858.     .B  -- returns p_byte
  1859.     .W  -- returns p_word
  1860.     .L  -- returns p_long
  1861.     . anything else -- returns p_unknown, and unreads the last token
  1862.                excluding the period.  It also prints out the
  1863.                warning that the period was ignored.
  1864.  
  1865.  ****************************************************************************/
  1866.  static p_size_type LOCAL p_dotsize ( char * line    ,
  1867.                       int  * line_offset ,
  1868.                       int    line_len ,
  1869.                       int    prev_offset )
  1870. {
  1871.   p_tok_type  tok     ;
  1872.  
  1873.   if ( curtok == '.' ) {
  1874.     prev_offset = *line_offset ;
  1875.     tok = next_token( line , line_offset, line_len);
  1876.     if ( (tok == symbol )&&
  1877.      ( curtoksym.sym[1] == NULL ) )
  1878.        switch ( curtoksym.sym[0] ) {
  1879.       case 'B': return p_byte ;
  1880.       case 'W': return p_word ;
  1881.       case 'L': return p_long ;
  1882.       }
  1883.     e_message(prev_offset,70," '.' ignored.");
  1884.     *line_offset = prev_offset ;
  1885.     return p_unknown ;
  1886.     }
  1887.   else {
  1888.     *line_offset = prev_offset ;
  1889.     return p_unknown ;
  1890.     }
  1891. } /* p_dotsize */
  1892.  
  1893.  
  1894.  
  1895. /*****************************************************************************
  1896.  
  1897.  Function parse_line - parse line
  1898.  
  1899.     This function parses a line into its components following the
  1900.     diagram :
  1901.  
  1902.      <---------------------------feedback------------|
  1903.     |                             |
  1904.     |  symbol ---> p_symbol  ------------------>','--|
  1905.                       |---->';'---->
  1906.                       |---->eoln--->
  1907.                       |---->error-->
  1908.  
  1909.        symbol           - any valid key word of SYMMAXLEN length or less.
  1910.        string literal  - quoted string. "example of string."
  1911.        eoln           - end of line reached
  1912.        error           - error in parsing
  1913.  
  1914.     Input parameters :
  1915.        line        - the line to be parsed.
  1916.  
  1917.  ****************************************************************************/
  1918.  static void LOCAL p_parse_line ( char *   line     , int  * line_offset    ,
  1919.                   int       line_len , p_node_type *  p_node ,
  1920.                   char *   done     )
  1921. {
  1922.   p_tok_type   tok ;
  1923.  
  1924.   tok = next_token( line , line_offset , line_len) ;
  1925.   p_node->p_nodeclass = pn_empty ;
  1926.   p_node->symptr = NULL ;
  1927.  
  1928.   switch ( tok ) {
  1929.      case symbol :
  1930.            p_node->p_nodeclass = pn_sym ;
  1931.            p_symbol(line, line_offset, line_len , TRUE,
  1932.                 '+',&(p_node->symptr),p_node );
  1933.            break;
  1934.      case token  : switch ( curtok ) {
  1935.               case ';' : *done = TRUE ;  /* Comment found */
  1936.                  break;
  1937.               default  : e_message(*line_offset,3,NULL);
  1938.                  *done = TRUE ;
  1939.                  break;
  1940.               }
  1941.            break ;
  1942.      case none     : *done = TRUE ;   /* End of line found */
  1943.            break ;
  1944.      default     : e_message(*line_offset,100,NULL);
  1945.      }
  1946.  
  1947.   *done = ( e_error.state ) ? TRUE:*done ;
  1948.  
  1949.   if ( ! *done ) {
  1950.   tok = next_token( line , line_offset , line_len) ;
  1951.   switch ( tok ) {
  1952.      case symbol :
  1953.        e_message(*line_offset,2,curtoksym.sym); /* Unexpected symbol */
  1954.        *done = TRUE ;
  1955.        break ;
  1956.      case token  :
  1957.        switch ( curtok ) {
  1958.           case ',' : break;
  1959.           case ';' : *done = TRUE ;              /* Comment found */
  1960.              break;
  1961.           default  : e_message(*line_offset,3," ',' or ; exp.");
  1962.              *done = TRUE ;
  1963.              break;
  1964.           }
  1965.        break ;
  1966.      case none     : *done = TRUE ;   /* End of line found */
  1967.            break ;
  1968.      default     : e_message(*line_offset,100,NULL);
  1969.            *done = TRUE ;
  1970.      }
  1971.   }
  1972.  
  1973.   if ( e_error.state ) p_node->p_nodeclass = pn_error ;
  1974.  
  1975. } /* p_parse_line */
  1976.  
  1977.  
  1978.  
  1979. /*****************************************************************************
  1980.    Function p_assem_line
  1981.  
  1982.      This function returns parses one line into a list of terms.
  1983.  
  1984.      Input parameters :
  1985.     line  : the line of source text to be parsed.
  1986.  
  1987.      Variable parameters
  1988.     label     : the label found for the line.
  1989.     command  : the command found on the line.
  1990.     size     : the size specification of the command. ie. MOVE.B is p_byte.
  1991.     numterms : the number of terms parsed.
  1992.     termlist : the term list in order of parsing
  1993.  
  1994.  ****************************************************************************/
  1995.  void p_assem_line ( char       * line           ,
  1996.              char         label[MAXSYMLEN]  ,
  1997.              char         command[MAXSYMLEN],
  1998.              p_size_type   * size           ,
  1999.              char       * numterms           ,
  2000.              am_term_type ** termlist           )
  2001.  
  2002.  {
  2003.   register int     i    ;
  2004.   int         line_offset = 0, line_len;
  2005.   char         done    ,  mode,  reg ;
  2006.   am_term_type * term , * prev ;
  2007.   p_tok_type     tok    ;
  2008.   p_node_type     p_node ;         /* current parse node. */
  2009.  
  2010.   e_error.state = 0 ;
  2011.  
  2012.   /* clear the parameters */
  2013.   label[0] = 0 ;
  2014.   command[0] = 0 ;
  2015.   *size = p_unknown ;
  2016.   *numterms = 0 ;
  2017.   *termlist = NULL ;
  2018.  
  2019.   line_len = strlen(line) ;
  2020.  
  2021.   /* read the line label */
  2022.   if ( isalpha( line[0] ) ) {
  2023.       /* scan the line to remove the label string. */
  2024.       tok = next_token( line , &line_offset , line_len) ;
  2025.       if (tok == symbol)
  2026.      strncpy( label,curtoksym.sym,MAXSYMLEN );
  2027.       }
  2028.  
  2029.   /* read the command */
  2030.   i = line_offset ;
  2031.   tok = next_token( line , &line_offset , line_len) ;
  2032.   switch ( tok ) {
  2033.      case symbol : strncpy(command,curtoksym.sym,MAXSYMLEN);
  2034.            /* check for size identifier */
  2035.            i = line_offset ;
  2036.            tok = next_token( line , &line_offset , line_len) ;
  2037.            switch ( tok ) {
  2038.               case symbol : line_offset = i ; break ;
  2039.               case token  : *size = p_dotsize(line,&line_offset,
  2040.                               line_len,i);
  2041.                     break;
  2042.               case none   : line_offset = i ; break ;
  2043.               }
  2044.            break ;
  2045.      case token  :
  2046.      case none     : line_offset = i; break ;
  2047.      }
  2048.  
  2049.   i = 0 ;
  2050.   done = FALSE ;
  2051.   while ( ! done ) {
  2052.      p_parse_line( line, &line_offset,line_len,&p_node,&done);
  2053.      if ( e_error.state ) break ;
  2054.      if ( p_node.p_nodeclass == pn_empty ) break ;
  2055.  
  2056.      switch ( p_node.p_nodeclass ) {
  2057.     case  pn_sym   : mode = 7 ;
  2058.            reg = ( am_abs_address_size == 1 )?0:1 ; break ; /* sym    */
  2059.     case  pn_empty : mode = 7 ; reg = 5     ; break ; /* empty/none*/
  2060.     case  pn_error : mode = 7 ; reg = 11     ; break ; /* error  */
  2061.     }
  2062.  
  2063.      term = ( am_term_type * ) malloc ( sizeof( am_term_type ) ) ;
  2064.      if ( !term ) {
  2065.      e_message(0,41,"Parser");   /* not enough memory */
  2066.      break ;
  2067.      }
  2068.  
  2069.      term->modereg = ( mode << 8 ) | reg ;
  2070.      term->symptr = p_node.symptr ;
  2071.      term->next = NULL ;
  2072.      term->prev = NULL ;
  2073.      term->sizeofreserve = 0 ;
  2074.      term->postword = 0 ;
  2075.      term->class = am_other_instr_term ;
  2076.  
  2077.      (*numterms)++ ;
  2078.      if ( !(*termlist) )  /* if termlist = NULL */
  2079.        *termlist = term  ;
  2080.      else {
  2081.        term->prev = prev ;
  2082.        prev->next = term ;
  2083.        }
  2084.      prev = term ;
  2085.  
  2086.      } /* while */
  2087.  
  2088.   if ( *termlist )
  2089.      (*termlist)->class = am_first_instr_term ;
  2090.  
  2091.  } /* p_assem_line */
  2092.  
  2093. [LISTING EIGHT]
  2094.  
  2095.  
  2096.   /*
  2097.      68000 Pseudo Module.
  2098.      This module contains those procedures needed to handle
  2099.      psuedo command assembly.
  2100.   */
  2101.  
  2102.   #include <stdio.h>
  2103.   #include <stdlib.h>
  2104.   #include <string.h>
  2105.  
  2106.   #include "68defs.h"
  2107.   #include "68err.h"
  2108.   #include "68parse.h"
  2109.   #include "68list.h"
  2110.   #include "68assem.h"
  2111.   #include "68symtab.h"
  2112.   #include "68pseudo.h"
  2113.  
  2114.  
  2115.   typedef struct  {  char       pseudo[10];
  2116.              ps_pseudos    index  ;
  2117.           } ps_pseudo_type ;
  2118.  
  2119.   ps_pseudo_type  ps_pseudo_array [7] =
  2120.                 { { "ABS_LONG"  , ps_abslong } ,
  2121.                   { "ABS_SHORT" , ps_absshort} ,
  2122.                   { "END"       , ps_end     } ,
  2123.                   { "EQU"       , ps_equate  } ,
  2124.                   { "EXTERN"    , ps_extern  } ,
  2125.                   { "GLB"       , ps_global  } ,
  2126.                   { "ORG"       , ps_origin  } } ;
  2127.  
  2128.  
  2129.  
  2130. /*****************************************************************************
  2131.    Function ps_lookup_pseudo
  2132.  
  2133.      This function looks up a pseudo command in the pseudo array and
  2134.      returns the psuedo class type if it is a valid pseudo command.
  2135.  
  2136.      Input parameter :
  2137.     pseudo    : the pseudo command being looked up.
  2138.      Variable parameter :
  2139.     pseudo_class : the class which is returned.
  2140.  
  2141.      Return code
  2142.     0 : pseudo not found.
  2143.     1 : pseudo found.
  2144.  
  2145.  ****************************************************************************/
  2146.  int ps_lookup_pseudo ( char       * pseudo      ,
  2147.             ps_pseudos * pseudo_class )
  2148.  {
  2149.   ps_pseudo_type * indx ;
  2150.  
  2151.   if ( indx = ( ps_pseudo_type * ) bsearch( pseudo, ps_pseudo_array, 7,
  2152.                      sizeof(ps_pseudo_type),strcmp) ){
  2153.      *pseudo_class = indx->index ;
  2154.      return 1 ;
  2155.      }
  2156.   else
  2157.      return 0 ;
  2158.  
  2159.  } /* ps_lookup_pseudo */
  2160.  
  2161.  
  2162. /*****************************************************************************
  2163.    Function ps_one_symbol_only
  2164.  
  2165.      This function returns true if the symbol list contains only one
  2166.      symbol, and no literals.  Otherwise, it returns false.
  2167.  
  2168.      For an empty list it returns false.
  2169.  
  2170.      Used by am_pseudo for EXTERNAL and GLOBAL operand validation.
  2171.  
  2172.      Input parameter :
  2173.     symlist : the symbol list
  2174.  
  2175.  ****************************************************************************/
  2176.  int ps_one_symbol_only ( p_sym_type * symlist )
  2177.  {
  2178.    if ( symlist )
  2179.       if ( symlist->next )
  2180.      return FALSE ;
  2181.       else
  2182.      if ( symlist->sym[0] )
  2183.         return TRUE ;
  2184.      else
  2185.         return FALSE ;
  2186.    else
  2187.      return FALSE ;
  2188.  } /* ps_one_symbol_only */
  2189.  
  2190.  
  2191.  
  2192. /*****************************************************************************
  2193.    Function ps_validate_pseudo
  2194.  
  2195.      This function validates the pseudo commands depending on the
  2196.      parameters passed in as listed below :
  2197.      Note : each action is based on a TRUE value for the variable.
  2198.      locationeven     : If location counter not even then error.
  2199.      onetermonly     : If number of terms not equal to one then an
  2200.                error results.
  2201.      zeroterms     : If number of terms >= 1 then error.
  2202.      labelrequired     : If there is no label then error.
  2203.      ignorlable     : If there is a label then warning.
  2204.      forwardsallowed : If symbols are forward referenced then no error
  2205.                else error
  2206.      stringsallowed  : If a term is found which is a string then
  2207.                 no error is issued.
  2208.      onesymbolonly     : Each term can only have one unresolved symbol
  2209.                in it or an error will result.
  2210.  
  2211.   Input parameters :
  2212.      label      : pointer to label found on current line.
  2213.      numterms : the number of terms in the termlist.
  2214.  
  2215.   Variable parameter
  2216.      termlist : Pointer to the term list.  The termlist is
  2217.             deleted in the event of an error.
  2218.  
  2219.   Note : On any error, the termlist is completely deleted, and
  2220.      the appropriate error message is in the error list created
  2221.      by e_message.
  2222.  
  2223.   Return code
  2224.      0    : validated.
  2225.      other : error in validation.
  2226.  
  2227.  ****************************************************************************/
  2228.  int  ps_validate_pseudo ( char      * label    ,
  2229.                char        numterms ,
  2230.                am_term_type ** termlist ,
  2231.                char        onetermonly      ,
  2232.                char        zeroterms      ,
  2233.                char        forwardsallowed,
  2234.                char        labelrequired  ,
  2235.                char        ignorlabel      ,
  2236.                char        stringsallowed ,
  2237.                char        onesymbolonly  )
  2238.  
  2239.  {
  2240.   am_term_type * term ;
  2241.   p_sym_type   * sym  ;
  2242.   int         i   , mode, reg  ;
  2243.  
  2244.  
  2245.  
  2246.   if ( ( onetermonly && ( numterms > 1 )) ||
  2247.        ( zeroterms   && ( numterms     )) ) {
  2248.      e_message(0,15,NULL) ;            /* requires one operand */
  2249.      i = 15 ;
  2250.      goto deleteterms ;
  2251.      }
  2252.  
  2253.   if ( onetermonly && ( numterms == 0 ) ) {
  2254.      e_message(0,13,NULL) ;            /* requires at least one operand */
  2255.      return 13 ;
  2256.      }
  2257.  
  2258.   if ( labelrequired && !(*label) ) {
  2259.      e_message(0,16,NULL) ;           /* label required */
  2260.      i = 16 ;
  2261.      goto deleteterms ;
  2262.      }
  2263.  
  2264.   if ( ignorlabel && *label )
  2265.      e_message(0,72,NULL ) ;            /* label ignored. */
  2266.  
  2267.   if ( numterms )
  2268.      /* validate each term */
  2269.      for  ( term = *termlist ; term ; term= term->next ) {
  2270.     mode = term->modereg >> 8 ;
  2271.     reg  = term->modereg &  15 ;
  2272.     if (  ( mode == 7 ) && ( reg <= 1 ) ) {
  2273.        if ( onesymbolonly ) {
  2274.           if ( ! ps_one_symbol_only(term->symptr)  ) {
  2275.          e_message(0,11,NULL)  ; /* illegal term */
  2276.          i = 11 ;
  2277.          goto deleteterms ;
  2278.          }
  2279.           sym_add_operand_symbol( term->symptr, term, FALSE );
  2280.           if ( ( term->symptr->sym[0] == '*' ) ||
  2281.            ( !term->symptr->sym[0] )) {
  2282.            e_message(0,19,NULL);
  2283.            i = 19 ;
  2284.            goto deleteterms ;
  2285.            }
  2286.           }
  2287.        else {
  2288.           for ( sym = term->symptr ; sym ; sym = sym->next ) {
  2289.          if ( sym->sym[0] )
  2290.             sym_add_operand_symbol( sym, term, FALSE );
  2291.  
  2292.          }
  2293.           /* compress symbol chain and hold onto number of unresolved */
  2294.           /* symbols in postword                      */
  2295.           term->postword = am_resolve_symbol(term->symptr) ;
  2296.  
  2297.           if ( ( ! forwardsallowed ) && ( term->postword ) ) {
  2298.          e_message(0,14,NULL) ;     /* cannot forward reference */
  2299.          i = 14 ;
  2300.          goto deleteterms ;
  2301.          }
  2302.           }
  2303.        }
  2304.     else {
  2305.        if ( ! ( stringsallowed && ( mode == 7 ) && ( reg == 10 ) )) {
  2306.          e_message(0,11,NULL)  ; /* illegal term */
  2307.          i = 11 ;
  2308.          goto deleteterms ;
  2309.          }
  2310.        }
  2311.     }
  2312.  
  2313.  
  2314.   return 0 ;
  2315.  
  2316.  
  2317.   deleteterms :        /* label for exit with deletion of terms */
  2318.                /* i will contain the error code returned*/
  2319.  
  2320.   if ( numterms )              /* delete all terms */
  2321.     am_delete_terms( termlist, TRUE );
  2322.  
  2323.   return i ;
  2324.  
  2325.  
  2326.  } /* ps_validate_pseudo */
  2327.  
  2328.  
  2329.  
  2330.  
  2331. /*****************************************************************************
  2332.   Function ps_pseudo
  2333.  
  2334.      This function handles all the pseudo commands for the assembler.
  2335.  
  2336.   Input parameters :
  2337.      label      : pointer to label found on current line.
  2338.      index      : the pseudo index
  2339.      size      : the size specification of the pseudo command. ( ie .B )
  2340.      numterms : the number of terms in the termlist.
  2341.  
  2342.   Variable parameter
  2343.      termlist : Pointer to the term list.  The termlist is
  2344.             deleted except for those terms which are not
  2345.             resolved for the particular pseudos which allow
  2346.             forward references.
  2347.   Globals :
  2348.     am_location_counter : updated when pseudo requires it.
  2349.     am_end_found        : set to TRUE if 'end' pseudo is encountered.
  2350.     am_abs_address_size : set to 1 for abs_short, 2 for abs_long.
  2351.  
  2352.   Note : On any error, the termlist is completely deleted, and
  2353.      the appropriate error message is in the error list created
  2354.      by e_message.
  2355.  
  2356.   Return code
  2357.      0    : pseudo handled okay.
  2358.      other : error occured.
  2359.  
  2360.  ****************************************************************************/
  2361.  int  ps_pseudo ( char        * label    ,
  2362.           ps_pseudos      index    ,
  2363.           char          numterms ,
  2364.           am_term_type ** termlist ,
  2365.           char        * line       )
  2366.  {
  2367.   am_term_type * term  ;
  2368.   int         i     ;
  2369.   l_line_type  * lptr  ;
  2370.  
  2371.   switch ( index ) {
  2372.      case ps_abslong : case ps_absshort :
  2373.      case ps_even    : case ps_end    :
  2374.       l_addline( l_neither , 0, line, &lptr); /* add line to listing */
  2375.       if ( *label ) e_message(0,72,NULL)    ; /* label ignored. */
  2376.       if ( numterms ) e_message(0,73,NULL)    ; /* operands ignored. */
  2377.       switch ( index ) {
  2378.          case ps_abslong  : am_abs_address_size = 2 ; break ;
  2379.          case ps_absshort : am_abs_address_size = 1 ; break ;
  2380.          case ps_even     : if ( am_location_counter & 1 )
  2381.                    am_location_counter++ ;
  2382.                 break;
  2383.          case ps_end      : am_end_found = TRUE ; break ;
  2384.          }
  2385.       am_delete_terms( termlist, TRUE ) ;
  2386.       return 0 ;
  2387.  
  2388.      case ps_equate :
  2389.       l_addline(l_neither,0,line,&lptr) ;
  2390.       /* requires one term only. label required. */
  2391.       if ( i = ps_validate_pseudo ( label, numterms, termlist,
  2392.                TRUE,FALSE,FALSE,TRUE,FALSE,FALSE,FALSE))
  2393.          return i ;
  2394.  
  2395.       /* add label and value to symbol table */
  2396.       i = sym_add_label_symbol( label, (*termlist)->symptr->val,
  2397.                     am_absolute ) ;
  2398.       am_delete_terms( termlist, TRUE ) ;
  2399.       return i ;
  2400.  
  2401.      case ps_extern :
  2402.       l_addline(l_neither,0,line,&lptr) ;
  2403.       /* one or more terms. label ignored. one symbol per each term */
  2404.       if ( i = ps_validate_pseudo ( label, numterms, termlist,
  2405.                FALSE,FALSE,FALSE,FALSE,TRUE,FALSE,TRUE))
  2406.          return i ;
  2407.  
  2408.       /* Add each symbol to the external symbol list. */
  2409.       for ( term = *termlist ; term ; term = term->next )
  2410.           if ( i = sym_add_extern(term->symptr->sym) ) {
  2411.          am_delete_terms( termlist, TRUE );
  2412.          return i ;
  2413.          }
  2414.  
  2415.       am_delete_terms( termlist, TRUE );
  2416.       return 0 ;
  2417.  
  2418.      case ps_global :
  2419.       l_addline(l_neither,0,line,&lptr) ;
  2420.       /* one or more terms. label ignored. one symbol per each term */
  2421.       if ( i = ps_validate_pseudo ( label, numterms, termlist,
  2422.                 FALSE,FALSE,FALSE,FALSE,TRUE,FALSE,TRUE))
  2423.          return i ;
  2424.  
  2425.       /* Add each symbol to the global symbol list. */
  2426.       for ( term = *termlist ; term ; term = term->next )
  2427.           if ( i = sym_add_global(term->symptr->sym) ) {
  2428.          am_delete_terms( termlist, TRUE );
  2429.          return i ;
  2430.          }
  2431.  
  2432.       am_delete_terms( termlist, TRUE );
  2433.       return 0 ;
  2434.  
  2435.      case ps_origin :
  2436.       l_addline(l_neither,0,line,&lptr) ;
  2437.       /* requires one term only. label ignored. */
  2438.       if ( i = ps_validate_pseudo ( label, numterms, termlist,
  2439.                    TRUE,FALSE,FALSE,FALSE,TRUE,FALSE,FALSE))
  2440.          return i ;
  2441.       am_location_counter = (*termlist)->symptr->val ;
  2442.       am_delete_terms( termlist, TRUE );
  2443.       return 0 ;
  2444.      }
  2445.  
  2446.   return 0 ;
  2447.  
  2448.  } /* ps_pseudo */
  2449.  
  2450.  
  2451. [LISTING NINE]
  2452.  
  2453.  
  2454.   /*
  2455.      68000 Symbol table Module.
  2456.      This module contains those procedures needed to handle
  2457.      the symbol table.
  2458.   */
  2459.  
  2460.   #include <stdio.h>
  2461.   #include <stdlib.h>
  2462.   #include <string.h>
  2463.  
  2464.   #include "68defs.h"
  2465.   #include "68err.h"
  2466.   #include "68parse.h"
  2467.   #include "68list.h"
  2468.   #include "68assem.h"
  2469.   #include "68symtab.h"
  2470.  
  2471.  
  2472.   /* Module level variable */
  2473.   sym_label_type    *  sym_glb_lab_head = NULL ;
  2474.   sym_label_type    *  sym_local_lab_head = NULL ;
  2475.  
  2476.   sym_operand_type  *  sym_ext_ref_head = NULL ;
  2477.   sym_operand_type  *  sym_local_ref_head = NULL ;
  2478.  
  2479.  
  2480.   /* Local prototypes */
  2481.  
  2482.   sym_operand_type * sym_lookup_extern( char  *  symbol );
  2483.   sym_label_type   * sym_lookup_local ( char  *  symbol );
  2484.   int             sym_addtolocaloplist ( p_sym_type      * symptr  ,
  2485.                         am_term_type  * termptr ) ;
  2486.  
  2487.  
  2488. /*****************************************************************************
  2489.   Function sym_add_symtabtolisting
  2490.  
  2491.      This function is meant to be called at the end of assembly.
  2492.      It adds the symbol tables to the listing if there are any
  2493.      global, external, or local symbols .
  2494.  
  2495.      The symbol table is of the following form :
  2496.  
  2497.        Symbol         Value     Class
  2498.        ----------    ------     ------
  2499.        MYSYMBOL      000000     Global  Relative
  2500.        MYSYMBOL1     00FFFF     Global  Absolute
  2501.        MYSYMBOL2     ??????     Global  Unknown
  2502.        MYSYMBOL3     001ABC     Local     Relative
  2503.        MYSYMBOL4     ??????     Extern  Unknown
  2504.  
  2505.   Calls
  2506.      l_addline : to add the text lines to the listing.
  2507.  
  2508.  ****************************************************************************/
  2509.   void sym_add_symtabtolisting()
  2510.   {
  2511.     register  sym_label_type   * labptr ;
  2512.     sym_operand_type * refptr ;
  2513.     l_line_type      * lptr  ;
  2514.     char         * message_buf , * chptr , count ;
  2515.     int            i ;
  2516.  
  2517.  
  2518.     if ( sym_glb_lab_head || sym_local_lab_head || sym_ext_ref_head ) {
  2519.        l_addline(l_neither, 0,"",&lptr);
  2520.        l_addline(l_neither, 0,
  2521.           " Symbol                Value     Class ",&lptr);
  2522.        l_addline(l_neither, 0,
  2523.           " ----------------      ------    -------",&lptr);
  2524.        }
  2525.     else
  2526.        return ;
  2527.  
  2528.     message_buf = "                                                      " ;
  2529.  
  2530.     for (count = 1 ; count <= 2 ; count++ ) {
  2531.        if ( count == 1 ) {
  2532.       strncpy(message_buf+33,"Global ",7);
  2533.       labptr = sym_glb_lab_head ;
  2534.       }
  2535.        else {
  2536.       strncpy(message_buf+33,"Local  ",7);
  2537.       labptr = sym_local_lab_head ;
  2538.       }
  2539.  
  2540.        for ( ; labptr ; labptr= labptr->next ) {
  2541.  
  2542.       i = strlen( labptr->symbol );
  2543.       strncpy( message_buf + 1, labptr->symbol, i );
  2544.       for ( chptr = message_buf + i + 1 ; i < MAXSYMLEN ; i++, chptr++ )
  2545.            *chptr = ' ' ;
  2546.  
  2547.       if ( labptr->relative == '?' ) {
  2548.          strncpy( message_buf + 23 ,"??????",6 );
  2549.          strncpy( message_buf + 43," Unknown  ", 10 );
  2550.          }
  2551.       else {
  2552.          sprintf( message_buf+ 23 ,"%06lX",labptr->val );
  2553.          message_buf[29] = ' ' ;
  2554.          if ( labptr->relative == '*' )
  2555.         strncpy( message_buf + 43," Relative ", 10 );
  2556.          else
  2557.         strncpy( message_buf + 43 ," Absolute ", 10 );
  2558.          }
  2559.       l_addline(l_neither, 0, message_buf , &lptr );
  2560.  
  2561.       }
  2562.        }
  2563.  
  2564.     strncpy( message_buf + 23, "??????",6 );
  2565.     strncpy( message_buf + 33, "Extern ",7);
  2566.     strncpy( message_buf + 43, " Unknown  ", 10 );
  2567.     for ( refptr = sym_ext_ref_head ; refptr ; refptr= refptr->next ) {
  2568.     i = strlen( refptr->symbol );
  2569.     strncpy( message_buf + 1, refptr->symbol, i );
  2570.     for ( chptr = message_buf + i + 1 ; i < MAXSYMLEN ; i++, chptr++ )
  2571.          *chptr = ' ' ;
  2572.     l_addline(l_neither, 0, message_buf , &lptr );
  2573.     }
  2574.  
  2575.   } /* sym_add_symtabtolisting */
  2576.  
  2577.  
  2578.  
  2579. /*****************************************************************************
  2580.   Function sym_process_unresolved_locals
  2581.  
  2582.      This function is meant to be called at the end of assembly.
  2583.      It checks the local reference list to see if any unresolved
  2584.      symbols are left in it. If there are , it generates an unresolved
  2585.      symbol error for each symbol, then deletes the local reference list.
  2586.  
  2587.      It also manually places the errors into the listing following the
  2588.      lines where the unresolved symbol was referenced, as well as calls
  2589.      l_addline to add the error message(s) at the end of the listing.
  2590.  
  2591.      Returns the total number of unresolved symbols.
  2592.  
  2593.  ****************************************************************************/
  2594.   int  sym_process_unresolved_locals()
  2595.   {
  2596.     int            i  ;
  2597.     sym_ref_type     * reflistptr ;
  2598.     sym_operand_type * refptr ;
  2599.     sym_label_type   * glbptr ;
  2600.     l_line_type      * lptr , * temp_lptr;
  2601.     err_type         * error ;
  2602.     char           message_buf[100] ;
  2603.  
  2604.     i = 0 ;
  2605.  
  2606.     /* Process global label list first */
  2607.     for ( glbptr = sym_glb_lab_head ; glbptr ; glbptr= glbptr->next ) {
  2608.  
  2609.        if ( glbptr->relative != '?' )   /* if its known then go to next one */
  2610.       continue ;
  2611.  
  2612.        e_message(0,23, NULL );
  2613.        i ++ ;
  2614.  
  2615.        error = e_error.errptr ;
  2616.        strcpy( message_buf , error->message ) ;
  2617.        strcpy( message_buf+strlen(error->message), glbptr->symbol );
  2618.  
  2619.        l_addline(l_neither, 0, message_buf , &lptr );
  2620.  
  2621.        e_delete_errors() ;
  2622.        }
  2623.  
  2624.     /* Process the local reference list. */
  2625.     for ( refptr = sym_local_ref_head ; refptr ; refptr= refptr->next ) {
  2626.        e_message(0,23, refptr->symbol );
  2627.        i ++ ;
  2628.  
  2629.        error = e_error.errptr ;
  2630.        strcpy( message_buf , error->message ) ;
  2631.        strcpy( message_buf+strlen(error->message), refptr->symbol );
  2632.  
  2633.        l_addline(l_neither, 0, message_buf , &lptr );
  2634.        l_addline(l_neither, 0, message_buf , &lptr );
  2635.  
  2636.        /* unlink the second redundant line */
  2637.        lptr->next->prev = lptr->prev ;
  2638.        lptr->prev->next = lptr->next ;
  2639.  
  2640.        /* link the second line into listing where symbol was first referenced */
  2641.        temp_lptr = refptr->list->termptr->lineptr ;
  2642.        lptr->next = temp_lptr->next ;
  2643.        lptr->next->prev = lptr ;
  2644.        lptr->prev = temp_lptr ;
  2645.        temp_lptr->next = lptr ;
  2646.  
  2647.        e_delete_errors() ;
  2648.        }
  2649.  
  2650.     /* delete the local reference list */
  2651.     while ( refptr = sym_local_ref_head ) {
  2652.     while ( reflistptr = refptr->list ) {
  2653.        refptr->list = refptr->list->next ;
  2654.        free( reflistptr );
  2655.        }
  2656.     sym_local_ref_head = refptr->next ;
  2657.     free( refptr );
  2658.     }
  2659.  
  2660.     return i ;
  2661.  
  2662.   } /* sym_process_unresolved_locals */
  2663.  
  2664.  
  2665. /*****************************************************************************
  2666.   Function sym_delete_all_tables
  2667.      This function deletes all the symbol tables.
  2668.   Globals
  2669.      sym_glb_label_head, sym_local_lab_head
  2670.      sym_ext_ref_head  , sym_local_ref_head   : all set to NULL when
  2671.                         the tables are deleted.
  2672.  
  2673.  ****************************************************************************/
  2674.  void sym_delete_all_tables( void )
  2675.  {
  2676.   sym_operand_type * refptr ;
  2677.   sym_label_type   * labptr ;
  2678.   sym_ref_type       * reflistptr ;
  2679.  
  2680.   /* delete the global label list */
  2681.   while ( labptr = sym_glb_lab_head ) {
  2682.       sym_glb_lab_head = labptr->next ;
  2683.       free( labptr );
  2684.       }
  2685.  
  2686.   /* delete the local label list */
  2687.   while ( labptr = sym_local_lab_head ) {
  2688.       sym_local_lab_head = labptr->next ;
  2689.       free( labptr );
  2690.       }
  2691.  
  2692.   /* delete the local reference list */
  2693.   while ( refptr = sym_local_ref_head ) {
  2694.       while ( reflistptr = refptr->list ) {
  2695.      refptr->list = refptr->list->next ;
  2696.      free( reflistptr );
  2697.      }
  2698.       sym_local_ref_head = refptr->next ;
  2699.       free( refptr );
  2700.       }
  2701.  
  2702.   /* delete the external reference list */
  2703.   while ( refptr = sym_ext_ref_head ) {
  2704.       while ( reflistptr = refptr->list ) {
  2705.      refptr->list = refptr->list->next ;
  2706.      free( reflistptr );
  2707.      }
  2708.       sym_ext_ref_head = refptr->next ;
  2709.       free( refptr );
  2710.       }
  2711.  
  2712.  } /* sym_delete_all_tables */
  2713.  
  2714.  
  2715. /*****************************************************************************
  2716.   Function sym_resolve_back
  2717.  
  2718.      This function resolves all back references of a particular
  2719.      label symbol passed to it.
  2720.  
  2721.   Input :
  2722.      symptr : pointer to label symbol node with value already resolved.
  2723.  
  2724.  
  2725.  ****************************************************************************/
  2726.   void sym_resolve_back( sym_label_type  *  symptr )
  2727.   {
  2728.     unsigned int       i  ;
  2729.     sym_operand_type * temp, * prev ;
  2730.     sym_ref_type     * refptr , * refhead , * tempref ;
  2731.     p_sym_type         * sym ;
  2732.     am_term_type     * termptr ;
  2733.     int            warnings ;
  2734.  
  2735.     prev = NULL ;
  2736.     for ( temp = sym_local_ref_head ;
  2737.       temp && strncmp(temp->symbol,symptr->symbol, MAXSYMLEN ) ;
  2738.       prev = temp , temp = temp->next );
  2739.  
  2740.     if ( !temp ) return ;
  2741.                       /* if there are back references */
  2742.     if ( prev )            /* remove the operand node from list.*/
  2743.        prev->next = temp->next ;
  2744.     else
  2745.        sym_local_ref_head = temp->next ;
  2746.     refhead = temp->list ;
  2747.     free( temp ) ;
  2748.  
  2749.     refptr = refhead ;
  2750.     while ( refptr ) {
  2751.     termptr = refptr->termptr ;
  2752.     for ( sym = termptr->symptr ; sym ; sym= sym->next )
  2753.         if ( ! strncmp( sym->sym, symptr->symbol, MAXSYMLEN ) ) {
  2754.            /* if the symbol is in the symbol list of the term */
  2755.            /* then resolve it. */
  2756.            sym->val = symptr->val ;
  2757.            sym->sym[0] = symptr->relative ;
  2758.            sym->sym[1] = 1 ; /* for relative '*' put in count after it */
  2759.            }
  2760.     refptr = refptr->next ;
  2761.     }
  2762.  
  2763.     /* compress ref list so that only unique termlists are refered to. */
  2764.     refptr = refhead ;
  2765.     while ( refptr ) {
  2766.     termptr = refptr->termptr ;
  2767.     if ( termptr )
  2768.     switch ( termptr->class ) {
  2769.       case am_first_instr_term :
  2770.          /* get rid of any term pointers which refer to the */
  2771.          /* next term if its class is am_other_instr    */
  2772.          /* this works since instructions can have only 2 terms */
  2773.          if ( termptr->next )
  2774.         if ( termptr->next->class == am_other_instr_term ) {
  2775.            for ( tempref = refhead; tempref ; tempref=tempref->next)
  2776.                if ( tempref->termptr == termptr->next )
  2777.               tempref->termptr = NULL ;
  2778.            }
  2779.          break ;
  2780.       case am_other_instr_term :
  2781.          /* back up to first_instr_term and do same as in case above*/
  2782.          if ( termptr->prev )   /* there should always be a prev term */
  2783.         if ( termptr->prev->class == am_first_instr_term ) {
  2784.            for ( tempref = refhead; tempref ; tempref=tempref->next)
  2785.                if ( tempref->termptr == termptr->prev )
  2786.               tempref->termptr = NULL ;
  2787.            }
  2788.          refptr->termptr = termptr->prev ; /* set ptr to first term */
  2789.          break ;
  2790.       case am_data_term :
  2791.          /* resolve only one data term at a time. no compression */
  2792.          break;
  2793.       }
  2794.     refptr = refptr->next ;
  2795.     }
  2796.  
  2797.     /* resolve terms and dispose of reference list. */
  2798.     refptr = refhead ;
  2799.     while ( refptr ) {
  2800.        termptr = refptr->termptr ;
  2801.        tempref = refptr->next ;
  2802.        free(refptr);
  2803.        refptr = tempref ;
  2804.        if ( !termptr ) continue ;
  2805.  
  2806.        i=am_resolve_term( termptr,( termptr->class == am_data_term)?1:0);
  2807.  
  2808.        if ( !i ) {                 /* if all resolved */
  2809.       warnings = e_error.warnings ;
  2810.       am_backfill( termptr );
  2811.  
  2812.       if ( e_error.warnings > warnings )
  2813.          l_add_errors( termptr->lineptr );
  2814.  
  2815.       warnings = e_error.warnings ;
  2816.       am_remove_terms_from_list(&termptr) ;
  2817.       }
  2818.  
  2819.        }
  2820.  
  2821.   } /* sym_resolve_back */
  2822.  
  2823.  
  2824.  
  2825. /*****************************************************************************
  2826.   Function sym_lookup_global
  2827.  
  2828.      This function looks up a symbol in the global label list.
  2829.      If the symbol is found, it returns a pointer to its label node,
  2830.      otherwise it returns NULL.
  2831.  
  2832.   Input :
  2833.      symbol : pointer to global symbol string.
  2834.  
  2835.   Returns :
  2836.      described above.
  2837.  
  2838.  ****************************************************************************/
  2839.   sym_label_type * sym_lookup_global( char  *  symbol )
  2840.   {
  2841.     sym_label_type * temp ;
  2842.  
  2843.     for ( temp = sym_glb_lab_head ;
  2844.       temp && strncmp(temp->symbol,symbol, MAXSYMLEN ) ;
  2845.       temp = temp->next );
  2846.     return temp ;
  2847.  
  2848.   } /* sym_lookup_global */
  2849.  
  2850.  
  2851. /*****************************************************************************
  2852.   Function sym_add_global
  2853.  
  2854.      This function will add a symbol to the global list if the
  2855.      symbol is not already in the global list.
  2856.  
  2857.      The symbol string is copied into the label node on success.
  2858.  
  2859.   Note : The relative field of the label node is set to '?' which
  2860.      denotes that the symbol does not yet have a value.
  2861.  
  2862.   Input :
  2863.      symbol : pointer to the symbol string of MAXSYMLEN or less.
  2864.  
  2865.   Globals :
  2866.      sym_glb_lab_head : head pointer is updated when symbol is added
  2867.             to global list.
  2868.   Warnings issued :
  2869.      74  : Symbol already in global list. Ignored.
  2870.  
  2871.   Returns :
  2872.      0 : symbol was added okay.
  2873.      41: not enough memory to add to list.
  2874.  
  2875.  ****************************************************************************/
  2876.   int  sym_add_global( char  *    symbol )
  2877.   {
  2878.    sym_label_type * temp ;
  2879.  
  2880.    if ( sym_lookup_global( symbol ) ) {
  2881.       e_message(0,74,NULL) ;          /* WARNING symbol already in glb list.*/
  2882.       return 0 ;
  2883.       }
  2884.    if ( temp = ( sym_label_type * ) malloc ( sizeof(sym_label_type) ) ) {
  2885.       strncpy( temp->symbol, symbol, MAXSYMLEN );
  2886.       temp->relative = '?' ;
  2887.       temp->next = sym_glb_lab_head ;
  2888.       temp->val = 0L ;
  2889.       sym_glb_lab_head = temp ;
  2890.       return 0 ;
  2891.       }
  2892.    e_message(0,41,NULL) ; /* out of memory */
  2893.    return 41 ;
  2894.  
  2895.   } /* sym_add_global */
  2896.  
  2897.  
  2898.  
  2899. /*****************************************************************************
  2900.   Function sym_lookup_extern
  2901.  
  2902.      This function looks up a symbol in the external label list.
  2903.      If the symbol is found, it returns a pointer to its operand node,
  2904.      otherwise it returns NULL.
  2905.  
  2906.   Input :
  2907.      symbol : pointer to external symbol string.
  2908.  
  2909.   Returns :
  2910.      described above.
  2911.  
  2912.  ****************************************************************************/
  2913.   sym_operand_type * sym_lookup_extern( char  *  symbol )
  2914.   {
  2915.    sym_operand_type * temp ;
  2916.  
  2917.    for ( temp = sym_ext_ref_head ;
  2918.      temp && strncmp(temp->symbol,symbol, MAXSYMLEN ) ;
  2919.      temp = temp->next );
  2920.    return temp ;
  2921.  
  2922.   } /* sym_lookup_ext */
  2923.  
  2924.  
  2925.  
  2926. /*****************************************************************************
  2927.   Function sym_add_extern
  2928.  
  2929.      This function will add a symbol to the external list if the
  2930.      symbol is not already in the external list.
  2931.  
  2932.      The symbol string is copied into the operand node on success.
  2933.  
  2934.   Input :
  2935.      symbol : pointer to the symbol string of MAXSYMLEN or less.
  2936.  
  2937.   Globals :
  2938.      sym_ext_ref_head : reference head pointer is updated when symbol is added
  2939.             to external list.
  2940.   Warnings issued :
  2941.      75  : Symbol already in external list. Ignored.
  2942.  
  2943.   Returns :
  2944.      0 : symbol was added okay.
  2945.      41: not enough memory to add to list.
  2946.  
  2947.  ****************************************************************************/
  2948.   int  sym_add_extern( char  *    symbol )
  2949.   {
  2950.     sym_operand_type * temp ;
  2951.  
  2952.     if ( sym_lookup_extern( symbol ) ) {
  2953.        e_message(0,75,NULL) ;       /* WARNING symbol already in extern list.*/
  2954.        return 0 ;
  2955.        }
  2956.     if ( temp = ( sym_operand_type * ) malloc ( sizeof(sym_operand_type) ) ) {
  2957.        strncpy( temp->symbol, symbol, MAXSYMLEN );
  2958.        temp->next = sym_ext_ref_head ;
  2959.        temp->list = NULL ;
  2960.        sym_ext_ref_head = temp ;
  2961.        return 0 ;
  2962.        }
  2963.     e_message(0,41,NULL) ; /* out of memory */
  2964.     return 41 ;
  2965.  
  2966.   } /* sym_add_extern */
  2967.  
  2968.  
  2969.  
  2970. /*****************************************************************************
  2971.   Function sym_lookup_local
  2972.  
  2973.      This function looks up a symbol in the local label list.
  2974.      If the symbol is found, it returns a pointer to its label node,
  2975.      otherwise it returns NULL.
  2976.  
  2977.   Input :
  2978.      symbol : pointer to local symbol string.
  2979.  
  2980.   Returns :
  2981.      described above.
  2982.  
  2983.  ****************************************************************************/
  2984.   sym_label_type * sym_lookup_local( char  *  symbol )
  2985.   {
  2986.     sym_label_type * temp ;
  2987.  
  2988.     for ( temp = sym_local_lab_head ;
  2989.       temp && strncmp(temp->symbol,symbol, MAXSYMLEN ) ;
  2990.       temp = temp->next );
  2991.     return temp ;
  2992.  
  2993.   } /* sym_lookup_local */
  2994.  
  2995.  
  2996.  
  2997. /*****************************************************************************
  2998.   Function sym_add_label_symbol
  2999.  
  3000.      This function will add a label symbol to a local label list,
  3001.      or resolve an already defined global label following the
  3002.      algorithm :
  3003.  
  3004.      if label already in extern label list then error
  3005.      else
  3006.         if label already in global label list then
  3007.            if its already resolved then error
  3008.            else
  3009.           resolve it and all back references in local op list.
  3010.         else
  3011.            if label in local label list then error
  3012.            else
  3013.          resolve it and all back references in local op list.
  3014.  
  3015.      If the relative field is set to am_relative then the label is
  3016.      treated as a relative label, otherwise it is treated as an
  3017.      absolute label.
  3018.  
  3019.   Note : The relative field is set to '*' if relative or 0 for absolute.
  3020.  
  3021.   Input :
  3022.      symbol   : pointer to the symbol string of MAXSYMLEN or less.
  3023.      val      : the long value of the symbol.
  3024.      relative : specifies whether or not to treat the label as relative.
  3025.  
  3026.   Globals :
  3027.      sym_local_lab_head : Local label head pointer is updated when symbol is
  3028.               added to label list.
  3029.  
  3030.   Returns :
  3031.      0 : okay.
  3032.      17: Cannot resolve an external symbol locally.
  3033.      18: symbol was already resolved.
  3034.      41: not enough memory to add to list.
  3035.  
  3036.  ****************************************************************************/
  3037.   int  sym_add_label_symbol ( char        * symbol ,
  3038.                   unsigned long   val    ,
  3039.                   am_assem_type   relative )
  3040.   {
  3041.     sym_label_type  * temp ;
  3042.  
  3043.     if ( sym_lookup_extern( symbol ) ) {
  3044.        e_message(0,17,NULL) ;       /* label symbol already in extern list.*/
  3045.        return 17;           /* cannot resolve locally. */
  3046.        }
  3047.  
  3048.     if ( temp = sym_lookup_global( symbol ) ) {   /* in global list */
  3049.        if ( temp->relative == '?' ) {  /* not resolved yet. */
  3050.       temp->relative = ( relative == am_relative ) ? '*' : 0 ;
  3051.       temp->val = val ;
  3052.                     /* resolve back references */
  3053.       sym_resolve_back( temp );
  3054.       return 0 ;
  3055.       }
  3056.        else {
  3057.       e_message(0,18,NULL) ;  /* symbol already resolved */
  3058.       return 18 ;
  3059.       }
  3060.        }
  3061.  
  3062.     if ( sym_lookup_local( symbol ) ) {
  3063.        e_message(0,18,NULL) ;       /* symbol already resolved in local list.*/
  3064.        return 18 ;
  3065.        }
  3066.  
  3067.     if ( temp = ( sym_label_type * ) malloc( sizeof(sym_label_type)) ) {
  3068.        strncpy( temp->symbol, symbol, MAXSYMLEN );
  3069.        temp->relative = ( relative == am_relative ) ? '*' : 0 ;
  3070.        temp->next = sym_local_lab_head ;
  3071.        temp->val  = val ;
  3072.        sym_local_lab_head = temp ;
  3073.                    /* resolve back references */
  3074.        sym_resolve_back( temp );
  3075.        return 0 ;
  3076.        }
  3077.     else {
  3078.        e_message(0,41,NULL) ;  /* out of memory */
  3079.        return 41 ;
  3080.        }
  3081.  
  3082.   } /* sym_add_label_symbol */
  3083.  
  3084.  
  3085.  
  3086. /*****************************************************************************
  3087.   Function sym_add_operand_symbol
  3088.  
  3089.      This function will add a attempt to resolve a symbol contained
  3090.      within a symbol node, or set the reference pointers in the
  3091.      reference lists accordingly depending on whether or not the
  3092.      symbol's value is known. It follows this algorithm :
  3093.  
  3094.      if symbol already in extern label list then
  3095.         add reference to it into the extern list.
  3096.      else
  3097.         if symbol already in global label list then
  3098.            if its already resolved then
  3099.           resolve it
  3100.            else
  3101.           add reference to the symbol to local op list.
  3102.         else
  3103.            if symbol in local label list then
  3104.           resolve it
  3105.            else
  3106.           add reference to the symbol to local op list.
  3107.  
  3108.   Input :
  3109.      symptr   : pointer to the symbol node.
  3110.      termptr  : pointer to term list that contains the symbol node.
  3111.      addref   : boolean flag indicating whether or not a reference
  3112.         pointer should be set up if the symbol is not yet
  3113.         resolved.
  3114.          if TRUE then reference pointers will be set up.
  3115.          if FALSE then reference pointers will not be set up.
  3116.   Note    : If addref is set to FALSE then no error can result since
  3117.       no memory allocation is attempted. Therefore the caller
  3118.       need not check the return code.
  3119.   Calls :
  3120.      sym_addtolocaloplist : This adds the refernece pointers for later
  3121.                 resolution to the local operand list.
  3122.  
  3123.   Returns :
  3124.      0 : okay.
  3125.      41: not enough memory to add to list.
  3126.  
  3127.  ****************************************************************************/
  3128.   int  sym_add_operand_symbol ( p_sym_type    * symptr    ,
  3129.                 am_term_type  * termptr ,
  3130.                 char        addref    )
  3131.   {
  3132.     sym_operand_type * temp  ;
  3133.     sym_ref_type     * temp2 ;
  3134.     sym_label_type   * temp3 ;
  3135.  
  3136.     if ( temp = sym_lookup_extern( symptr->sym ) ) {  /* in extern list */
  3137.        if ( ! addref ) return 0 ;
  3138.        if ( temp2 = ( sym_ref_type * ) malloc( sizeof(sym_ref_type)) ) {
  3139.       temp2->termptr = termptr ;
  3140.       temp2->next = temp->list ;
  3141.       temp->list = temp2 ;
  3142.       return 0 ;
  3143.       }
  3144.        else {
  3145.      e_message(0,41,NULL) ;   /* not enough memory */
  3146.      return 41 ;
  3147.      }
  3148.        }
  3149.  
  3150.     if ( temp3 = sym_lookup_global( symptr->sym ) ) {    /* in global list */
  3151.        if ( temp3->relative == '?' )   /* not resolved yet. */
  3152.       if ( ! addref ) return 0 ;
  3153.       else return sym_addtolocaloplist( symptr, termptr ) ;
  3154.        else {
  3155.       symptr->val = temp3->val ;              /* resolve the symbol */
  3156.       symptr->sym[0] = temp3->relative ;
  3157.       symptr->sym[1] = 1 ; /* set relative count to 1 */
  3158.       return 0 ;
  3159.       }
  3160.        }
  3161.  
  3162.     if ( temp3 = sym_lookup_local( symptr->sym ) ) {
  3163.        symptr->val = temp3->val ;             /* resolve the symbol*/
  3164.        symptr->sym[0] = temp3->relative ;
  3165.        symptr->sym[1] = 1 ;   /* set relative count to 1 */
  3166.        return 0 ;
  3167.        }
  3168.     else
  3169.        if ( ! addref ) return 0 ;
  3170.        else return sym_addtolocaloplist( symptr, termptr ) ;
  3171.  
  3172.   } /* sym_add_operand_symbol */
  3173.  
  3174.  
  3175.  
  3176. /*****************************************************************************
  3177.   Function sym_addtolocaloplist
  3178.  
  3179.      This function will add a symbol reference to the local operand
  3180.      list. If the symbol already has referneces in the local operand
  3181.      list, it creates a new reference node only. If the symbol has no
  3182.      previous references, it creates the symbol operand node as well
  3183.      as its first reference node.
  3184.  
  3185.   Input :
  3186.      symptr   : pointer to the symbol node.
  3187.      termptr  : pointer to term list that contains the symbol node.
  3188.  
  3189.   Globals :
  3190.      sym_local_ref_head : updated when symbol has never been referenced
  3191.               before, and a new operand node was created.
  3192.  
  3193.   Returns :
  3194.      0 : okay.
  3195.      41: not enough memory to add to list.
  3196.  
  3197.  ****************************************************************************/
  3198.   int  sym_addtolocaloplist ( p_sym_type    * symptr  ,
  3199.                   am_term_type  * termptr )
  3200.   {
  3201.     sym_operand_type  * temp ;
  3202.     sym_ref_type      * temp2 ;
  3203.  
  3204.     for ( temp = sym_local_ref_head ;
  3205.       temp && strncmp(temp->symbol,symptr->sym, MAXSYMLEN ) ;
  3206.       temp = temp->next );
  3207.     if ( temp )
  3208.        if ( temp2 = ( sym_ref_type * ) malloc( sizeof(sym_ref_type)) ) {
  3209.       temp2->termptr = termptr ;
  3210.       temp2->next = temp->list ;
  3211.       temp->list = temp2 ;
  3212.       return 0 ;
  3213.       }
  3214.        else {
  3215.       e_message(0,41,NULL) ; /* not enough memory */
  3216.       return 41 ;
  3217.       }
  3218.     else
  3219.        if ( temp = ( sym_operand_type * ) malloc (sizeof(sym_operand_type)) )
  3220.       if ( temp2 = ( sym_ref_type * ) malloc( sizeof(sym_ref_type)) ) {
  3221.          strncpy( temp->symbol, symptr->sym, MAXSYMLEN );
  3222.          temp->next = sym_local_ref_head ;
  3223.          temp->list = temp2 ;
  3224.          sym_local_ref_head = temp ;
  3225.          temp2->termptr = termptr ;
  3226.          temp2->next = NULL ;
  3227.          return 0 ;
  3228.          }
  3229.       else {
  3230.          free(temp);
  3231.          e_message(0,41,NULL);  /* not enough memory */
  3232.          return 41 ;
  3233.          }
  3234.        else {
  3235.       e_message(0,41,NULL);  /* not enough memory */
  3236.       return 41 ;
  3237.       }
  3238.  
  3239.  
  3240.   } /* sym_addtolocaloplist */
  3241.  
  3242.  
  3243.  
  3244.  
  3245.