//------------------------------------------------
// ASCIINUM.CPP
// Keith Larson
// TMS320 DSP Applications
// (c) Copyright 1995, 1996, 1997
// Texas Instruments Incorporated
//
// This is unsupported freeware code with no implied warranties or
// liabilities.  See the disclaimer document for details
//
//  DSK3A Assembler core revision history
//  -------------------------------------
//     1.00    02/01/96  apx   Keith Larson - Release version
//     1.01    05/15/96  apx   Keith Larson
//     1.02    06/15/96  apx   Keith Larson
//     1.03    07/10/96        Keith Larson
//     1.04    07/20/96        Keith Larson
//     1.10    08/05/96        Keith Larson - Release version
//----------------------------------------------------------
// ASSM_FUN.CPP contains most of the functions which are
// critical to both the assembler and debugger.  The assembler
// and debugger top level files are therefor a type of shell
// that calls the base routines contained herein.
//----------------------------------------------------------

// modified by Chi Duong to fit into C30sim code
// Version: @(#)assm_fun.cc	1.5	12/13/97

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

#include "opcodes.h"
#include "assm_fun.h"
#include "tmsfloat.h"
#include "symbols.h"
#include "errormsg.h"
#include "exp_anal.h"
#include "argsplit.h"
// #include "dsk.h"
#include "symbols.h"
// FILE *dsk_file; // chi
FILE *hex_file;
//float DSK3_rev      = 1.12; // Version of DSK3 code
char  *par_ops     =" || "; //
char  *par_ops2    = " ||"; //
char  if_stack[16]        ; //
char  if_on          =   1; //
char  W_GLBL_FLAG    =   0; //
int   if_level       =   0; //
int   g_pass         =   2; // Default of 2 is used for debugger
int   inc_lvl        =   0; //
int   obj_word_count =   0; //
int   looplevel      =   0;
#define  max_loop     1024  //
long  loop_count     =   0; //
ulong Global_Raw_Code=   0; // Raw code passed as global (klunky!)
int   warn_cnt       =   0;
int   err_cnt        =   0;
char  g_strg1[bufsize]; // globals are used to minimize parameter
char  g_strg2[bufsize]; // passing to the lower levels
char  g_file [bufsize]; //
int   g_line;           //
ulong ENTRY; //chi

//------------------------------------------------------
// obj_pack() takes each object sub-structure in the
// object and packs it into the final 32 bit value which
// is passed back to the caller.
//------------------------------------------------------
ulong obj_pack(OBJ &obj)
{ ulong o;
  o = obj.raw & obj.mask;
  if(obj.A.msk) o |= ((obj.A.val & obj.A.msk) << obj.A.shf);
  if(obj.B.msk) o |= ((obj.B.val & obj.B.msk) << obj.B.shf);
  if(obj.C.msk) o |= ((obj.C.val & obj.C.msk) << obj.C.shf);
  if(obj.D.msk) o |= ((obj.D.val & obj.D.msk) << obj.D.shf);
  if(obj.E.msk) o |= ((obj.E.val & obj.E.msk) << obj.E.shf);
  if(obj.F.msk) o |= ((obj.F.val & obj.F.msk) << obj.F.shf);
  if(obj.G.msk) o |= ((obj.G.val & obj.G.msk) << obj.G.shf);
  return o;
}
//-------------------------------------------------------
// floatreg() checks to see if the register number
// corresponds to valid floating point register
//-------------------------------------------------------
int floatreg(long x, int C3Xmode)
{
  if(C3Xmode)
  { if(x<8) return 1;
    else    return 0;
  }
  if((x<8) | (x>27)) return 1;
  else               return 0;
}
/*
//-------------------------------------------------------
// symb_defined() returns a non zero offset+1 into the
// the symbol table if a symbol is defined
//-------------------------------------------------------
int symb_defined(char *ptr)
{
  for(int x=0;x<last_ref;x++)
  {
    if(strexact(ptr, SYM[x].sptr))
      return x+1;  // Can now use to redefine a symbol
  }
  return 0;
}
*/
//--------------------------------------------------------
// ref_chg should only be called when the SYM offset to be
// changed has been previosly confirmed by a call to symb_defined()
//--------------------------------------------------------
MSGS xref_chg(ulong value, NUM_TYPE type, SYM_TYPE addressable, int x)
{
  SYM[x].value = value;
  SYM[x].numt  = type;
  SYM[x].symt  = addressable;
  return NO_ERR;  // ref not found
}
//--------------------------------------------------------
// isreg() checks to see if the string symbol is a valid
// register.  If it is the op pointer is filled with the
// correct information for code construction.
//--------------------------------------------------------
int isreg(char *ptr, operand &op, int shf, ulong msk)
{ char buf[bufsize];
  strncpy(buf,ptr,bufsize-1);
  ptr = buf;
  strupr(buf);
  int x;
  op.msk = msk;
  op.shf = shf;
  for(x=0;;x++)
  {
    if(C30Reg[x].v==-1) break;  // -1 is used to terminate table
    if(C30Reg[x].v >= 0)        // Skip C4x register codes
    {
      if(strexact(C30Reg[x].s, ptr))
      {
        op.val = C30Reg[x].v;
        return(x+1);
      }
    }
    else
    {
      if(C3Xmode==0)
      {
        if(strexact(C30Reg[x].s, ptr))
        {
          op.val = -C30Reg[x].v;
          return(x+1);
        }
      }
    }
  }
  return 0;
}
//-------------------------------------------------------------------
// dir_search() checks for an mnemonic field match of known directive
// strings.  If a directive is found an enumerated value is returned
// which can then be used in a switch statement to control the assembly
// process of the directive.
//
// NOTE: By ordering the directive search table with the most likely
// ecountered directives first, the asemblers speed is enhanced
//-------------------------------------------------------------------
MSGS dir_search(char *ptr)
{
  strlwr(ptr);
  if(strexact(ptr,".word"   )) return d_word;
  if(strexact(ptr,".float"  )) return d_float;
  if(strexact(ptr,".loop"   )) return d_loop;
  if(strexact(ptr,".endloop")) return d_eloop;
  if(strexact(ptr,".sdef"   )) return d_sdef;
  if(strexact(ptr,".long"   )) return d_long;
  if(strexact(ptr,".sfloat"  )) return d_sfloat;
  if(strexact(ptr,".float8"  )) return d_float8;
  if(strexact(ptr,".float16" )) return d_float16;
  if(strexact(ptr,".psfloat" )) return d_psfloat;
  if(strexact(ptr,".pfloat8" )) return d_pfloat8;
  if(strexact(ptr,".pfloat16")) return d_pfloat16;
  if(strstr  (ptr,".q"       )) return d_qxx;
  if(strexact(ptr,".if"     )) return d_if;
  if(strexact(ptr,".else"   )) return d_else;
  if(strexact(ptr,".endif"  )) return d_endif;
  if(strexact(ptr,".noload" )) return d_noload;
  if(strexact(ptr,".int"    )) return d_int;
  if(strexact(ptr,".hword"  )) return d_hword;
  if(strexact(ptr,".byte"   )) return d_byte;
  if(strexact(ptr,".set"    )) return d_set;
  if(strexact(ptr,".sect"   )) return d_sect;
  if(strexact(ptr,".ifdef"  )) return d_ifdef;
  if(strexact(ptr,".ifndef" )) return d_ifndef;
  if(strexact(ptr,".define" )) return d_define;
  if(strexact(ptr,".include")) return d_inc;
  if(strexact(ptr,".copy"   )) return d_inc;
  if(strexact(ptr,".ieee"   )) return d_ieee;
  if(strexact(ptr,".space"  )) return d_space;
  if(strexact(ptr,".fill"   )) return d_fill;
  if(strexact(ptr,".text"   )) return d_text;
  if(strexact(ptr,".data"   )) return d_data;
  if(strexact(ptr,".string" )) return d_string;
  if(strexact(ptr,".global" )) return W_GLBL;
  if(strexact(ptr,".globl"  )) return W_GLBL;
  if(strexact(ptr,".ref"    )) return W_GLBL;
  if(strexact(ptr,".def"    )) return W_GLBL;
  if(strexact(ptr,".entry"  )) return d_entry;
  if(strexact(ptr,".start"  )) return d_start;
  if(strexact(ptr,".end"    )) return d_end;
  if(strexact(ptr,".align"  )) return d_align;
  if(strexact(ptr,".brstart")) return d_bitrev;
  if(strexact(ptr,".xon"    )) {Enhanced_Enable = 1; return d_Enh_En;}
  if(strexact(ptr,".xoff"   )) {Enhanced_Enable = 0; return d_Enh_En;}
  return d_err;
}
//-------------------------------------------------------
// seg_num() is used to match .sect 'name' name strings
// to known sections.  The section number is then used
// to keep track of the current section address count
// during pass 2 assembly.
//-------------------------------------------------------
int seg_num(char *p)
{ for(int x=0;x<last_segment;x++)
    if(strexact(p,SEG[x].name))  return x;
  return -1;
}
//-------------------------------------------------------
// seg_add() adds the *ptr 'name' to the sections array
//-------------------------------------------------------
int seg_add(char *ptr, long addr)
{
  xref_add(ptr,addr,INTEGER,SYM_LBL);  // SYM_LBL was 1
  if(last_segment >= MAX_SEGMENTS) return -1;
  strcpy(SEG[last_segment].name,ptr);
  SEG[last_segment].offs = addr;
  SEG[last_segment].start = addr;
  last_segment++;
  return(last_segment-1);
}
//-------------------------------------------------------
// newseg() checks to see if *ptr 'name' is already
// defined and if not, creates a new section beginning
// at the given address.
//-------------------------------------------------------
MSGS newseg(int cmd, char *ptr)
{ // long temp; // chi
  int seg2;
  char *p;
  char buf[bufsize];
  strncpy(buf,ptr,bufsize-1);
  ptr = buf;
  if(*ptr!='"') return x_syntax;  // Name must be surrounded by quotes
  ptr++;
  if((p = strstr(ptr,"\""))==NULL) return x_syntax;
  *p=0;           // Quote termination is performed on copy of ptr
  //
  // .start directive defines a new segment, but does not switch to it
  //
  if(cmd == 3)
  //chi { switch(expressionz(arg[3].s,&(ulong)temp,INTEGER)) // new address
  { ulong(temp);
  switch(expressionz(arg[3].s,&temp,INTEGER)) // new address
    { case INTEGER: break;
      default     : return d_start;          // .start error return
    }
    if(seg_num(ptr)!=-1) return d_err_sect2; // segment already defined?
    if(seg_add(ptr,temp)==-1)                // add new segment
    {current_seg=0;return d_sect_add;}       // general error return
    return DIR_NO_ERR;
  }
  //
  // Switch to a defined segment  =>  .sect .data or .text directives
  //
  if(cmd == 1)
  { switch(seg2 = seg_num(ptr))
    { case -1: return d_err_sect1; // return if undefined section
      default: break;
    }
    current_seg = seg2;
    return DIR_NO_ERR;
  }
  return NO_REF;
}
//-----------------------------------------------------------
// dprintf() is a generic output for directives.  If the
// DSK3D global variable is set, the caller was a command
// line assembly statement, so the output goes to the
// physical device rather than a file.
//-----------------------------------------------------------
//chi void dprintf(long addr, long temp,char *ptr)
void dprintf( long temp)
{
  //if(DSK3D!=0)    putmem(addr,1,&(ulong )temp);
  //if(DSK3D==0)
  //{
    //chi fprintf(dsk_file,"0x%08lx 0x%08lx <%s>\n",addr,temp,ptr);
    fprintf(hex_file,"0x%08lx\n",temp);
  //}
}
//-----------------------------------------------------------
// cvrt_dir()  directive conversion routines
// This function is responsible for determining what directive
// is to be assembled and then performs the appropriate action
//-----------------------------------------------------------
MSGS cvrt_dir(char *ptr, int args, NUM_TYPE *DIR_TYPE)
{
  ulong   temp;
  ulong     t2;
  ulong     t3;
  ulong opcode;
  double db;
  int     bite;
  //chi int     shft;
  int     shft = 0;
  NUM_TYPE type;
  float f  = 0.0;
  float f2 = 0.0;
  char buf[bufsize];
  char range_warning = 0;
  MSGS err, err2;
  int x;
  char *p;
  OBJ obj;
  long *addr;
  addr = &SEG[current_seg].offs;
  err2=dir_search(arg[1].s);       // find and parse directive
  switch(err2)                     // find and parse directive
  {
    case d_Enh_En: return DIR_NO_ERR;
    //---------------------------------------------------------
    case W_GLBL : err2 = DIR_NO_ERR;
                  W_GLBL_FLAG = 1;
                  break;

    case d_if   : if_stack[if_level++] = if_on;
                  if(if_on == 0) return d_nocode;  // check if_stack[x-1]
                  if(if_level > IF_LVL_MAX) return d_if_lvl;
                  // Scan for ==,=,<,> relational operators.  If found
                  // split the statement into sub expressions and evaluate
                  //  NOTE: expressionz should be able to resolve this...
                  //      >> and << pass through unchanged
                  switch(expressionz(arg[2].s,&temp,AUTO))
                  {
                    case  INTEGER: if(temp != 0) if_on=1;
                                   else          if_on=0; return DIR_NO_ERR;
                    case  FLOAT:   f=*((float *)&temp);
                                   if(f != 0.0)  if_on=1;
                                   else          if_on=0; return DIR_NO_ERR;
                    case  NO_REF:  if_on=0; return DIR_NO_ERR;
                    default:       return d_if;  // Error in conversion
                  }
    //--------------------------------------------------------------
    // .ifdef and .ifndef are used to control assembly similar to the C
    // #ifdef and #ifndef.
    case d_ifndef:
    case d_ifdef: NON_COFF_MATH_F = 1;
                  if_stack[if_level++] = if_on;
                  if(if_on == 0) return d_nocode;  // check if_stack[x-1]
                  if(if_level > IF_LVL_MAX) return d_if_lvl;
                  if(ref_offs(arg[2].s)==-1) if_on = 0; // not defined
                  else                       if_on = 1;
                  if(err2==d_ifndef) if_on^=1;
                  return DIR_NO_ERR;/*
                  switch(expressionz(arg[2].s,&temp,AUTO))
                  {
                    default:       temp = 0;
                    case  INTEGER: if(temp != 0) if_on=1;
                                   else          if_on=0; return DIR_NO_ERR;
                    case  FLOAT:   f=*((float *)&temp);
                                   if(f != 0.0)  if_on=1;
                                   else          if_on=0; return DIR_NO_ERR;
                  }                   */
              //  if(!symb_defined(arg[0].s))
              //    ref_add(arg[2].s,0,INTEGER,0);
    case d_define: // Add symbol, knowing that it will be anihialated
                   // at the beginning of pass2
                   //          ptr value type addressable
                   err=xref_add(arg[2].s,0,NOREF,SYM_VAR);
                   switch(err)       //SYM_VAR was 0
                   {
                     case MULT_REF:
                     case NO_ERR  : return DIR_NO_ERR;
                     default      : return err;
                   }

    case d_else : if(if_stack[if_level-1] == 0) return d_nocode;
                  if_on ^= 1;
                  return DIR_NO_ERR;

    case d_endif: if(if_stack[if_level-1] == 0)
                  { if_level--; return d_nocode; }  // check if_stack[x-1]
                  if_level--;
                  if(if_level < 0) return d_if_lvl;
                  if_on = if_stack[if_level];
                  return DIR_NO_ERR;
    //chi
    default: break;
  }
  if(if_on==0) return d_nocode;

  switch(err2)
  {
    //--------------------------------------------------------
    case d_start: NON_COFF_MATH_F = 1;
                  return(newseg( 3,    arg[2].s));
    case d_sect : return(newseg( 1,    arg[2].s));
    case d_text : return(newseg( 1, "\".text\""));
    case d_data : return(newseg( 1, "\".data\""));


/*  case d_bss  : if((err=newseg( 1, "\".bss\""))!=NO_ERR) return err;
                  if((err=expressionz(arg[3].s,&temp,INTEGER)!=INTEGER)
                    if(g_pass==2)return dir;
                  ref_add(arg[2].s,*addr);
                  *addr += temp;
                  return DIR_NO_ERR;
*/
    //--------------------------------------------------------
    case d_entry: NON_COFF_MATH_F = 1;
                  switch(expressionz(arg[2].s,&ENTRY,INTEGER))
                  { case  FLOAT  : return err2;
                    case  INTEGER: break;
                    default:
                              if(*arg[2].s != 0)
                                if(g_pass==2) return err2;
                              ENTRY = SEG[current_seg].offs;
                              break;
                  }
                  return DIR_NO_ERR;
    //--------------------------------------------------------
    // .bitrev is used to create an aligned section starting at the
    // first valid bit-reversable address following the current section.
    // To avoid massive filling, the current section is NOT used.
    // The .brsect directive is therefor similar to .sect except that
    // the current section address is used as a start reference
    //
    // usage:
    //       .brsect  "br_secn",<size>  ; Create BR section
    //       .word    0,1,2,3           ; initialize some datae
    //       .sect    "old_sect"        ; go back to old section
    //        nop                       ;
    //       .sect    "br_secn"         ; go back to BR section
    //
    //--------------------------------------------------------
    case d_bitrev : NON_COFF_MATH_F = 1;
               if(args != 3) return err2;
               if(expressionz(arg[3].s,&temp,INTEGER) != INTEGER)
               /*if(g_pass==2)*/ // should always be valid expresn
               return err2;
               //
               // check to see if power of 2
               //
               t2 = temp ^ 0xFFFFFFFFL;  // 1's compliment
               // add should yield all 1's
               if(temp + t2 != 0xFFFFFFFFL) return d_bitrev;
               temp = ((*addr-1) & (t2+1)) + temp;
               //
               // Add br-section using .start, followed by .sect
               //
               sprintf(arg[3].s,"0x%08lx",temp);
               if((err2=newseg(3, arg[2].s))!=DIR_NO_ERR) return err2;
               if((err2=newseg(1, arg[2].s))!=DIR_NO_ERR) return err2;
               return DIR_NO_ERR;
    //-------------------------------------------------------------
    // .align is used to fill in the code up to a cache boundary
    // if the fill length is >4, the first opcode used is a
    // (B)ranch to the aligned boundary, otherwise fill with NOP's
    //-------------------------------------------------------------
    case d_align: temp = ((*addr-1) & 0xFFFFFFE0L) + 32;
                  t3 = *addr;
                  for(t2=*addr;t2<temp;t2++)
                  {
                    if( ((temp-t2)>4) && (t3 == t2))
                             sprintf(buf,"   B   0x%08lx ; align",temp);
                    else     sprintf(buf,"   NOP            ; align"     );
                    strcpy(g_file, "align");
                    assm(buf,"",obj);
                    if(g_pass==2)
                    { opcode = obj_pack(obj);
                        //chi fprintf(dsk_file,"0x%08lx 0x%08lx %s\n",
                        //chi               SEG[current_seg].offs,opcode);
                        fprintf(hex_file,"0x%08lx\n", opcode);
                    }
                    *addr+=1;
                  }
                  return DIR_NO_ERR;
    //--------------------------------------------------------
    case d_rstring:NON_COFF_MATH_F = 1;
                   return(err2);  // reverse string not implimented
    case d_string:
                   bite = 3;temp = 0; shft = 0;
                   for(x=2;x<args+1;x++)
                   {
                     ptr = arg[x].s;
                     if(*ptr=='\"')          // "style using quotes"
                     { ptr++;
                       for(int i=0;i<80;i++)   // 80 chars max
                       { switch(*ptr)
                         { case   0 :
                           case '"' : i=90; break;  //end of string
                           default:

                          //    t2 = *ptr++;
                          //    temp = temp | (t2 << shft);
                                //
                                // Still bug hunting!  The following line
                                // also causes a system crash due to pointer
                                // corruptions.  Source is unknown...
                                // register check shows ...
                                //
                                temp |= ((long)*ptr++ << shft);

                                shft+=8;
                                if(bite-- == 0)
                                { if(g_pass==2)
                                  {
                                    dprintf(temp);
                                  }
                                  bite = 3;
                                  temp = 0;
                                  shft = 0;
                                  *addr += 1;
                                } break;
                         }
                       }
                     }
                     else
                     { switch(expressionz(arg[x].s,&t2,INTEGER))
                       { case  INTEGER: //temp = temp >> 8;
                                  temp = temp | (t2 << shft);
                                  shft+=8;
                                  if(bite-- == 0)
                                  { if(g_pass==2)
                                    {
                                      dprintf(temp);
                                    }
                                    bite = 3;
                                    temp = 0;
                                    shft = 0;
                                    *addr += 1;
                                  } break;
                         default: //printf("STRING CONVERSION ERROR\n");
                                  if(g_pass==2)  // allow pass1 substitution
                                  return d_string;
                       }
                     }
                   }
                   if(bite != 3)
                   { if(g_pass==2)
                     {
                       dprintf(temp);
                     }
                     *addr += 1;
                   }
                   err2 = DIR_NO_ERR;
                   break;

   //-----------------------------------------------------------
    case d_qxx   : NON_COFF_MATH_F = 1;
                   if(g_pass==1) { *addr += (args -1); return DIR_NO_ERR; }
                   p = strstr(arg[1].s,".q");
                   db = strtod(p+2,&p);
                   if(*p!=0) return err2;
                   db = pow(2,db);
                   for(x=2;x<args+1;x++)
                   {
                     if(expressionz(arg[x].s,(ulong *)&f2,FLOAT)!=FLOAT)
                       if(g_pass==2) return err2;
                     //chi temp = db * f2;
                     temp = (long unsigned int)(db * f2);
                     if(g_pass==2)
                     dprintf(temp);
                     *addr += 1;
                   }
                   break;
    //--------------------------------------------------------------
    // When a .loop statement is encountered the loop count must be
    // resolvable in the first pass since the absolute address of all
    // symbols and opcodes must be determined within the first pass
    //
    // The method used for looping a section of code is to remember the
    // file offset at the end of the .loop directive.  Then by 'rewinding'
    // the input file to this address the file can be looped N times...
    //
    // The method used for signaling loop start and loop end is to return
    // the enumerated d_loop_start d_loop_end variables
    case d_loop:
    //chi if((expressionz(arg[2].s,&(ulong)loop_count,INTEGER))!=INTEGER)
    ulong(loop_count);
    if((expressionz(arg[2].s,&loop_count,INTEGER))!=INTEGER)
                  return err2; // loop expresions must resolve in pass1
                  //chi if(loop_count <        0) return err2; // default
                  if(loop_count > max_loop) return err2; // is 256
                  looplevel=(int)loop_count+1;
                  return d_loop_start;

    case d_eloop: looplevel--;
                  if(looplevel < 0) return d_if_lvl;
                  return d_loop_end;

    case d_inc  : return d_inc;
     /*
                  inc_lvl++;
                  if(inc_lvl > 8) return d_inc2;
                  while((p=strstr(arg[2].s,"\""))!=NULL) *p = ' ';
                  p = arg[2].s;
                  if(*p==' ') p++;
                  //
                  // Back up global names and strings
                  //
                  strcpy(temp_g_file,g_file);
                  strcpy(temp_g_strg1,g_strg1);
                  strcpy(temp_g_strg2,g_strg2);
                  temp_g_line = g_line;
                  if((err=passx(p,g_pass)) > NO_ERR) return err;
                  //
                  // Restore global names and strings
                  //
                  g_line = temp_g_line;
                  strcpy(g_file,temp_g_file);
                  strcpy(g_strg1,temp_g_strg1);
                  strcpy(g_strg2,temp_g_strg2);
                  inc_lvl--;
                  break;
    */
    case d_end  : return d_end;
    case d_set  : //if(g_pass==2) return d_nocode; // Done in pass1
                  if((*arg[0].s==0)||(*arg[2].s==0))
                    return BAD_ARG;

                  switch(type = expressionz(arg[2].s,&temp,AUTO))
                  {
                    case INTEGER: // reference stores float in ieee format
                    case FLOAT  : break;
                    default: if(g_pass==1) return d_nocode;
                             return err2;  // Error in conversion
                  }
                  if((g_pass==2) && symb_defined(arg[0].s)) return d_set2;
                  if((err=xref_add(arg[0].s,temp,type,SYM_VAR)) > NO_ERR)
                    return err;                    //SYM_VAR was 0
                  if(g_pass==2) return d_set2; // no err in pass2
                  return d_nocode;           // no err in pass1
    //--------------------------------------------------------
    // d_sdef is a special case of .set which allows a variable to be
    // defined multiple times without errors.  This allows a variable to
    // be redefined any number of times or to be used as a .include to
    // such as in a header file of library modules.
    //
    //  - Used in conjunction with d_ifdef
    //
    //          .ifdef   __inc_1x
    //__inc_1x  .symdef  1
    //          ;-----------------
    //          ;USERS CODE
    //          ;-----------------
    //          .endif
    //
    //LOOPS  .symdef   4           ; Pass a macro variable to LOOP.ASM
    //       .include  "LOOP.ASM"
    //
    //
    //START
    //       .loop     4
    //X      .eval     $-START
    //       .word     X
    //       .endloop
    //
    case d_sdef:  NON_COFF_MATH_F = 1;
                  if((*arg[0].s==0)||(*arg[2].s==0)) return BAD_ARG;
                  switch(type = expressionz(arg[2].s,&temp,AUTO))
                  {
                    case INTEGER: // reference stores float in ieee format
                    case FLOAT  : break;
                    default: return d_set;  // Error in conversion
                  }
                  // Redefine the value if it is not already set
                  // otherwise, define the variable
                  if((x=symb_defined(arg[0].s))==0) //+ directive control
                  {
               //   int t;
               //   t = 0;                    // t=0 not addressable?
                    if((err=xref_add(arg[0].s,temp,type,SYM_DEF)) > NO_ERR)
                       return err;              // SYM_DEF was 2
                  }
                  else  // Symbol redefine
                  {
                //  int t;
                //  t = SYM[x].a;          // t = old addressable type?
                    if((err=xref_chg(temp,type,SYM_DEF,x-1)) > NO_ERR)
                       return err;          //SYM_DEF was 2
                  }
                  return d_nocode;           // no err in pass1
    //--------------------------------------------------------
    case d_float8  :
    case d_sfloat  :
    case d_float16 :
    case d_psfloat :
    case d_pfloat8 :
    case d_pfloat16:

    case d_ieee : NON_COFF_MATH_F = 1;
    case d_float: if(args < 2) return err2;
                  *DIR_TYPE = FLOAT;
                  if(g_pass==1)
                  {
                    args--;
                    switch(err2)                     // NOTE: Packed pfloat
                    {                                // terminates with zero
                      case d_pfloat8 : args = args>>2; break;
                      case d_psfloat :
                      case d_pfloat16: args = args>>1; break;
                      default        : break;
                    }
                    *addr += args;
                    return DIR_NO_ERR;
                  }

                  strncpy(buf,ptr,bufsize-1);
           //     ptr = buf;
                  for(x=2;x<args+1;x++)
                  { // expression analysis returns IEEE float

                    if(expressionz(arg[x].s,(ulong *)&f,FLOAT) != FLOAT)
                      if(g_pass==2) return err2;
             //     f+=0.1;

                    switch(err2) // convert floats to correct format
                    {
                      case d_pfloat16:temp= TMS_C32_16(IEEE_TMS(f));
                                      if( (x&1)==0) {t2 = 0; shft=16;}
                                      t2 = t2 | (temp<<shft);
                                      shft-=16;
                                      if(((x&1)==1)||(x==args))
                                        dprintf(t2);
                                      else
                                        *addr-=1;//dec-inc fix addr loop
                                      break;
                      case d_psfloat: temp= TMS_TMS_SHORT(IEEE_TMS(f));
                                      if( (x&1)==0) {t2 = 0; shft=16;}
                                      t2 = t2 | (temp<<shft);
                                      shft-=16;
                                      if(((x&1)==1)||(x==args))
                                        dprintf(t2);
                                      else
                                        *addr-=1;//dec-inc fix addr loop
                                      break;

                      //
                      // Make a float8 & float16 function for tables!
                      //
                      case d_pfloat8: temp=IEEE_TMS(f);
                                      temp= TMS_C3X_VSHORT(temp);
                                      if( (x&3)==2) {t2 = 0; shft=24;}
                                      t2 = t2 | (temp<<shft);
                                      shft-=8;
                                      if(((x&3)==1)||(x==args))
                                        dprintf(t2);
                                      else
                                        *addr-=1;//dec-inc fix addr loop
                                      break;

                      case d_float8:  temp=IEEE_TMS(f);
                                      temp= TMS_C3X_VSHORT(temp);
                                      dprintf(temp);
                                      break;

                      case d_sfloat:  temp=IEEE_TMS(f);
                                      temp= TMS_TMS_SHORT(temp);
                                      dprintf(temp);
                                      break;

                      case d_float16: temp= TMS_C32_16(IEEE_TMS(f));
                                      dprintf(temp);
                                      break;

                      case d_float  : dprintf(IEEE_TMS(f));
                                      break;

                      case d_ieee   : dprintf(*((ulong *)&f));
                                      break;
		      //chi
		      default: break;
                    }
                    *addr += 1;
                  }
                  break;
    //--------------------------------------------------------
    case d_noload: *addr += (args -1); return DIR_NO_ERR;
    case d_word :
    case d_int  :
    case d_long :
    case d_byte :
    case d_hword: if(args < 2) return err2;
                  if(g_pass==1)
                  {
                    *addr += (args -1);
                    return DIR_NO_ERR;
                  } // if label
                  strncpy(buf,ptr,bufsize-1);
                  ptr = buf;
                  for(x=2;x<args+1;x++)
                  { // expression analysis returns integer
                    if(expressionz(arg[x].s, &temp,INTEGER) != INTEGER)
                      if(g_pass==2) return err2;
                    switch(err2)
                    {
                      case d_byte:
                           if((temp&0xFFFFFF00L)!=0x00000000L)//val+
                           if((temp&0xFFFFFF00L)!=0xFFFFFF00L)//val-
                             range_warning=1;
                           dprintf(temp&0xff);
                           break;
                      case d_hword:
                           if((temp&0xFFFF0000L)!=0x00000000L)//val+
                           if((temp&0xFFFF0000L)!=0xFFFF0000L)//val-
                             range_warning=1;
                           dprintf(temp & 0xffff);
                           break;
                      case d_word   : dprintf(temp); break;
                      case d_int    : dprintf(temp); break;
                      case d_long   : dprintf(temp); break;
                      default: break;
                    }
                    *addr += 1;
                  }
                  break;

    case d_space: if(expressionz(arg[2].s,&temp,INTEGER)!=INTEGER)
                     if(g_pass==2)return err2;
                  if((long)temp > 1024) return err2;
                  if((long)temp <    0) return err2;
                  if(g_pass==1) { *addr += temp; return DIR_NO_ERR;}
                  for(;temp>0;temp--)
                  {
                    if(g_pass==2)
                    dprintf(0);
                    *addr += 1;
                  }
                  break;

    case d_fill:  if(expressionz(arg[2].s,&temp,INTEGER)!=INTEGER)
                     if(g_pass==2)return err2;
                  if((long)temp > 1024) return err2;
                  if((long)temp <    0) return err2;
                  if(*arg[3].s==0) return err2;
                  if(expressionz(arg[3].s,&t2,INTEGER)!=INTEGER)
                     if(g_pass==2)return err2;
                  if(g_pass==1) { *addr += temp; return DIR_NO_ERR;}
                  for(;temp>0;temp--)
                  {
                    if(g_pass==2)
                    dprintf(t2);
                    *addr += 1;
                  }
                  break;

    case d_err  :
    default     : return err2; // error detected
  }
  if(range_warning) return range_warn;
  return DIR_NO_ERR;
}
//--------------------------------------------------------------------
// assm() inputs are the code string(s) to assemble and a pointer
// to the object being created.
//
// On return an enumerated error (int) is returned which can then be
// used to output an error string associated with the error.
//--------------------------------------------------------------------
MSGS assm(char *strg1, char *strg2, OBJ &obj)
{
  int x;
  NUM_TYPE DIR_TYPE = INTEGER;
  char *tp, *tp2;
  MSGS err;
  char buf1[bufsize], buf2[bufsize], buf3[bufsize];
  long old_addr;
  MSGS err2;
  int parcode = 0;
  //
  // Check for NULL and comment only assembly lines
  //
  if((strg1==NULL) || (*strg1=='*') || (*strg1==';'))
    return NO_CODE;
  //
  // If || in column one of strg2, move line to right, with leading WS
  //
  if((*strg2=='|') && (*(strg2+1)=='|'))
  {
    strncpy(buf1,strg2,bufsize-1);
    *strg2 = ' ';
    strcpy(strg2+1,buf1);
  }
  //
  // Split line into delimited arguments
  x = pack_and_split_arg(strg1);
  if(x == -1) return NO_CODE;
  if(x == -2) return EXPR_ERR; //NO_CODE;
  if(strstr(arg[1].s,"||")!=NULL)
  {
    if(if_on)
      return PAR_ERR;
  }
  //
  // Process directives and or convert them into objects
  if(*arg[1].s=='.')
  {
    strcpy(g_strg2,nullstring);  // all directives fit on one line
    if(g_pass==2)
    if(loop_count<=1)
    {
      /* if_on test added 3/7/97 after finding that the loader was
         misidentifying .sect statements that where in fact not valid
      */
      /*
      if(if_on)
        sprintf(buf2 ,"0x%08lx directive  %s\n",SEG[current_seg].offs,g_strg1);
      else
        sprintf(buf2 ,"0x%08lx nocode     %s\n",SEG[current_seg].offs,g_strg1); 
     
      if(DSK3D==0)
      {
        fprintf(dsk_file,"%s",buf2);
      } 
      */
      //chi
      if(strstr(strg1, ".sect")) {
	sprintf(buf2, "0x%08lx  Dummy Section size\n",SEG[last_segment-1].offs);
	fprintf(hex_file,"%s",buf2);
	sprintf(buf2, "0x%08lx  Start Address '%s'\n",SEG[last_segment-1].start,SEG[last_segment-1].name);
	fprintf(hex_file,"%s",buf2);
      }
    }
    old_addr = SEG[current_seg].offs;
    err2 = cvrt_dir(strg1,x,&DIR_TYPE);
    //
    // if a loop is detected, return control to assm() calling function
    // (in this case usually passx()).  In turn this function will
    // create and control the nested loop
    //
    switch(err2)
    {
      case d_inc: return err2;
      case d_loop_start: return err2;
      case d_loop_end  : return err2;
      case d_end: if(loop_count != 0) return d_eloop;
                  return err2;
      case d_err: return d_err;
      case d_nocode:  break; // nulls and false if/else/endif return
      case d_set: if(x != 2) return err2;
                  if(g_pass==1) break;
                  assm_error_msg(err2,"");
                  break;
      case d_set2:break; // pass2 exit for .set values set in pass1
      case DIR_NO_ERR:
                  if((g_pass==1) && valid_label_char(arg[0].s))
                  { if(bad_label(arg[0].s))        return d_err;

//       if((err=xref_add(arg[0].s,old_addr,DIR_TYPE,SYM_LBL)) > NO_ERR)
         if((err=xref_add(arg[0].s,old_addr,DIR_TYPE,SYM_LBL)) > NO_ERR)

                      return err;                    //SYM_LBL was 1
                  }
                  break;
      default   : err = assm_error_msg(err2,"");
                  //if(DSK3D && (err!=NO_ERR))
                    //return err;
                  break;
    }
    return DIR_NO_ERR;
  }
  if(if_on == 0)   return NO_CODE;
  if((g_pass==1) && valid_label_char(arg[0].s))
  {
    if(bad_label(arg[0].s)) return d_err;
    if((err=xref_add(                            // SYM_LBL was 1
    arg[0].s,(ulong &)SEG[current_seg].offs,INTEGER,SYM_LBL)) > NO_ERR)
       return err;
  }
  if(x < 1) return NO_CODE;
  //
  // split and repack the first line
  strncpy(buf2,arg[1].s,bufsize-1); // copy opcode to buf2
  strcpy(arg[0].s, " ");     // repack line args, removing labels and opcode
  strcpy(arg[1].s, " ");     //
  repack_args(buf1);
  //
  // Check 2nd line ||, If found split, repack and append
  if((tp=strstr(strg2,"||")) != NULL)
  {
    tp2 = strstr(strg2,";");
    if((tp2==NULL) || (tp2 > tp)) // Is || operation only a comment?
     {
      pack_and_split_arg(strg2); // unpack strg2
      if(strstr(arg[1].s,"||") == NULL) return PAR_ERR;
      *arg[1].s = 0;
//    strcat(buf2," || ");       // append parallel code
      strcat(buf2,par_ops);       // append parallel code
      strcat(buf2,arg[2].s);
//    strcat(buf1," ||");        // split indicator
      strcat(buf1,par_ops2);        // split indicator
      //
      // Add label in parallel line if it is present
      // Need to add this code!
      if((g_pass==1) && valid_label_char(arg[0].s))
      {
        if(bad_label(arg[0].s)) return d_err;
        if((err=xref_add(                             //SYM_LBL was 1
        arg[0].s,(ulong &)SEG[current_seg].offs,INTEGER,SYM_LBL)) > NO_ERR)
          return err;
      }
      //
      strcpy(arg[0].s, " ");     // null out label, || and code
      strcpy(arg[1].s, " ");
      for(x=2;x<maxargs;x++)
        strcpy(arg[x].s,arg[x+1].s);
      repack_args(buf3);         // repack and append
      strcat(buf1,buf3);
      parcode = 1;
    }
  }
  // Search for opcode match
  if(*buf2 != 0)
  { strupr(buf2);                      // Use UC for search
    for (x=0; ; x++)
    { if(strexact(Instr[x].instr ,buf2)) break;
      if(Instr[x].en == __word)
      {
        if(strlen(buf2) < 10) return BAD_OPCD;
        return BAD_OPCD; //FATAL_OPC;
      }
    }
  }
  //if(C3Xmode)
  //
    //if(isC4code(x)) return BAD_ARG;
  //}
  // From a valid string match, the opcode form is known
  // clear each mask field before calling aform(n)
  obj.A.msk = 0; obj.B.msk = 0; obj.C.msk = 0; obj.D.msk = 0;
  obj.E.msk = 0; obj.F.msk = 0; obj.G.msk = 0;
  //
  //  CALL THE APPROPRIATE ASSEMBLER FORM
  //
  if(g_pass==1)
  {
    if(parcode == 0) return NO_ERR;
    return PAR_NO_ERR;
  }
  if(*buf1 != 0)
  //
  // To most easily impliment the enhanced indirect addressing
  // modes of the C3x/C4x (not available in early PG's) the
  // top 4 bits of the raw binary code is analyzed.  A global
  // variable is used to pass the raw code to the field parser.
  //
  Global_Raw_Code = Instr[x].pattern;
  err = Instr[x].aform( x, obj, buf1);
  return err;
}
MSGS assm_error_msg(MSGS err,char *strg)
{
  char *p, *b;
  char buf[bufsize+bufsize+80];
  MSGS rtrn = ERROR;
  //if(DSK3D)
    //return err;
  if((err==NO_ERR)||(err==DIR_NO_ERR)) return NO_ERR;
  p = Error_Strg(err);
  if(strstr(p,"WARN")) {warn_cnt++; err_cnt--; rtrn = WARNING;}
  err_cnt++;
  b = buf;
  b+=sprintf(b,">>>> %-12s line %d: ERROR %d  WARNING %d\n"
                    ,g_file,g_line,err_cnt,warn_cnt);
  if(strg!="") b+=sprintf(b,">>>> %s %s\n",strg,p);
  else         b+=sprintf(b,">>>> %s\n",p);
  b+=sprintf(b,">>>> %s\n",g_strg1);
  if(strstr(g_strg2,"||")!=NULL)
  b+=sprintf(b,">>>> %s\n",g_strg2);
  b+=sprintf(b,">>>>\n");
  printf (      buf);
  //chi fprintf(dsk_file,buf);
  fprintf(hex_file,buf);
  //chi if(strstr(p,"FATAL")) { fcloseall(); exit(1); } // return FATAL
  if(strstr(p,"FATAL"))   C30SimCloseAllAssemblerFiles();  // return FATAL
  return rtrn;
}
