//
// File: assmc30.cc
//
// This file is a replacement for the C30 DSK file dsk3a.cpp.  We
// wrote the dskAssembler function to take the place of the main program
// of dsk3a.cpp.  The dskAssembler function generates two files with
// .inthex and .hex extensions.  We removed PC dependencies.  We
// also mimicked the Borland C++ function fcloseall.
//
//------------------------------------------------------------
// DSK3A.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.CPP is a DOS text window based program which will
//  assemble DSK formatted source files into DSK files
//
//  The project file should include the following files in a directory
//  tree similar to the one shown below.
//
//                  (root)
//
//  C:\---\DSK3SRC--+--\DSK3--+-- \DSK3A  (not used for DSK3D)
//                  |         +-- \DSK3D
//                  |         +-- \COMMON
//                  |         +-- \COMMON2
//                  |         +-- \DSK3APPS
//                  |
//                  +-- Verxxx
//                  +-- Veryyy
//                      etc...

// #include <conio.h>   // chi
#include <ctype.h>
// #include <dos.h>     // chi
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include "assm_fun.h"
#include "symbols.h"
#include "errormsg.h"
#include "argsplit.h"
#include "opcodes.h"

#include "tmsfloat.h" //chi
#include "asupport.h" //chi

#define lname 256          // max length for filename

//---------------------------------------------------------------
// init_segments() initializes the structure defining segment
// allocation to a default state before assembling a file
//---------------------------------------------------------------
void init_segments(void)
{ int seg;
  OBJ obj;
  for(seg=0;seg<MAX_SEGMENTS;seg++)
  { strncpy(SEG[seg].name,"",16);
    SEG[seg].name[15] = 0;
    SEG[seg].offs=0;
  }
  seg = 0;
  last_segment = 0;
  strcpy(g_file,"setup");
  g_line = 0;
  g_pass = 1;
  assm(" .start  \"Default_sect\",0x809802","",obj);
  assm(" .sect   \"Default_sect\"","",obj);
  if_level = 0;
  if_on = 1;
  err_cnt = 0;
  warn_cnt = 0;
}
//---------------------------------------------------------------------
// if a symbol is neither a label, fixed or float (0,1,2) it is a
// temporary variable which needs to be destroyed between pass 1 & 2
//---------------------------------------------------------------------
void anihilate_xset(void)
{ for(int x=0;x<last_ref;x++)
    if(SYM[x].symt == SYM_DEF)    // was addressable=2 // delate 2nd pass
      SYM[x].sptr = nullstring;
}                 // was 2

//-------------------------------------------------------------------
// passx() sets up and controls the assembly process for the 1st and
// 2nd pass.  This is where each line is read from the input file and
// passed to the assm() function.  Enumerated returns from assm()
// identify things like no_code, directives, code, looping etc...
//-------------------------------------------------------------------
//MSGS passx(char *name, char pass)
MSGS passx(char* name, char pass, int err_limit,
           int warn_limit, char* subDirectory)
{
  MSGS err;
  fpos_t loopfilepos,filepos;
  OBJ obj;              // object structure filled in by assembler
  int loop_line,line = 1;         // local file line count
  FILE *stream = 0;    // local file name
  char mstrg[bufsize]; // Message string
  char strg1[bufsize]; // 1st line
  char strg2[bufsize]; // 2nd line
  char dstrg1[bufsize]; // 1st line - packed
  char dstrg2[bufsize]; // 2nd line - packed
  char *buf = 0;        // GP pointer
  char *p = 0;
  char assmlines;
  long opcode; //,old_addr;
  char temp_g_strg1[bufsize];
  char temp_g_strg2[bufsize];
  char temp_g_file [lname];
  char incname[lname];
  int temp_g_line;

  // Open source file
  strncpy(g_file, name, bufsize);
  stream = fopen(name, "rt");
  // begin chi
  // look in the sub-directory if we can't open the file in current directory
  if (stream == NULL) {
    char tempName[lname];
    strncpy(tempName, subDirectory, lname);
    strncat(tempName, name, lname - strlen(subDirectory));
    strncpy(g_file, tempName, bufsize);
    stream = fopen(tempName, "rt");
  }
  // end chi
  if (stream == NULL)
  {
    sprintf(strg1,">>>> FATAL ERROR: >> CANNOT OPEN %s\r\n",name);
    err_cnt++;
    fprintf(hex_file,strg1);
    fprintf(stderr,
            "error in reading an input file; see .inthex file for detail.\n");
    fflush(stderr);
    //chi fcloseall();
    C30SimCloseAllAssemblerFiles();
    return FATAL; 
  }
  else 
    C30SimRegisterAssemblerFile(stream);

  fseek(stream, 0, SEEK_SET);                  // seek to start of file
  // Since parallel opcodes span two lines, the loop is unrolled
  // such that the line being assembled is the previous line
  if((buf=fgets(strg1,bufsize-1, stream)) == NULL)
  {
    fclose(stream);
    C30SimUnregisterAssemblerFile(stream);
  }
  fgetpos(stream,&filepos);
  loop_line = line;
  buf=fgets(strg2,bufsize-1, stream);
  for(line=1;;line++)
  {
    //-----------------------------------------------------------
    // Split up arguments and repack
    //-----------------------------------------------------------
    if((p=strstr(strg1,"\n"))!=NULL) *p=0;  // strip newline character
    if((p=strstr(strg1,"\r"))!=NULL) *p=0;  // strip line return character
    if((p=strstr(strg2,"\n"))!=NULL) *p=0;// strip \n character
    if((p=strstr(strg2,"\r"))!=NULL) *p=0;// strip \r character

    strcpy( dstrg1,strg1); strcpy( dstrg2,strg2);
    strcpy(g_strg1,strg1); strcpy(g_strg2,strg2);

    g_line  = line;
    g_pass  = pass;
    //------------------------------------------------------------
    assmlines = 1;
    err=assm(dstrg1,dstrg2, obj);
    switch(err)
    {
      case d_inc: //err = passx(arg[2].s,g_pass); break;
                  inc_lvl++;
                  if(inc_lvl > 8)
                  {
                    fclose(stream);
                    C30SimUnregisterAssemblerFile(stream);
                    err_cnt++;
                    assm_error_msg(d_inc2,"");
                    return d_inc2;
                  }
                  while((p=strstr(arg[2].s,"\""))!=NULL) *p = ' ';
                  //
                  // Remove leading/trailing WS during copy...
                  //
                  p = arg[2].s;
                  if(*p==' ') p++;
                  if(p==0)  return d_inc;   // No file string == error
                  strncpy(incname,p,lname);
                  while((p=strstr(incname," "))!=NULL) *p = 0;
                  p=incname;
                  //
                  // 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(g_pass == 1)
                  {
                    sprintf(mstrg,"\n");
                    //chi fprintf(dsk_file,mstrg);
                    fprintf(hex_file,mstrg);
                  }
                  //
                  //chi if((err=passx(p,g_pass,err_limit,warn_limit)) != NO_ERR)
                  if(((int)err=passx(p,g_pass,err_limit,warn_limit,subDirectory)) != NO_ERR)
                  {
                    fclose(stream);
                    C30SimUnregisterAssemblerFile(stream);
                    return err;
                  }
                  if(g_pass == 1)
                  {
                    sprintf(mstrg,"\n");
                    //chi fprintf(dsk_file,mstrg);
                    fprintf(hex_file,mstrg);
                  }
                  // 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_loop_start: loopfilepos=filepos;
                         loop_line = line;
                         assmlines = 1;
                         break;
      case d_loop_end  : loop_count--;
                         assmlines = 1;
                         if(loop_count <= 0) break;
                         line = loop_line;
                         fsetpos(stream,&loopfilepos);
                         fgets(strg2,bufsize-1, stream);
                         //
                         // If file ends with .endloop buf will be NULL
                         // To keep program from inadvertantly exiting
                         // buf is set to point to something that should
                         // exist if everything is working properly
                         //
                         buf = strg2;  // 6/1/96
                         //
                         break;

      case NO_CODE: if(pass==1) break;

               break;
      case PAR_NO_ERR: assmlines += 1; line++;
      case NO_ERR:
             if(pass==2)
             {
               opcode = obj_pack(obj);

               //chi fprintf(dsk_file,"0x%08lx \n",opcode);
               fprintf(hex_file,"0x%08lx \n",opcode);

             }
             SEG[current_seg].offs+=1;
             break;
      case DIR_NO_ERR: break;  // Listing output and objects are written
                               // by the directive processor in assm()
      case d_end: //  buf = strg2;
                  break;
      default   : // print out specific error message
                  if(strstr(g_strg2,"||")) assmlines = 2;
                  if(assmlines==1)  strcpy(g_strg2,nullstring);
                  err = assm_error_msg(err,"");
                  if(err_cnt   > err_limit) {err=d_end; break;};
                  if(warn_cnt > warn_limit)
                  { err_cnt++;
                    err = d_end;
                  }
                  break;
    }

    if(err==d_end) break;
    if(err_cnt >= err_limit) break;
    if(warn_cnt >= warn_limit) break;
    fgetpos(stream,&filepos);
    if(assmlines == 1)
    { if(buf==NULL) break;
      strcpy(strg1,strg2);
    }
    else
    {
      if((buf=fgets(strg1,bufsize-1, stream)) == NULL) break;
    }
    buf=fgets(strg2,bufsize-1, stream);
  }
  if(ENTRY==0)  // If zero, .entry was most likely not in the source
  {             // Furthermore, you cannot execute the reset vector!
    assm(" .entry 0x809802 ;Add Default entry ","",obj);
  }

  fclose(stream);
  C30SimUnregisterAssemblerFile(stream);
  if((inc_lvl==0) && err_cnt)
    return TOO_MANY_ERR;  // failed nested include return value
  if(err==d_end) return d_end;
  return NO_ERR;
}
//-------------------------------------------------------------------
// dskAssembler(..) replace dsk3a.cpp 
//-------------------------------------------------------------------
MSGS dskAssembler(int err_limit, int warn_limit,
                  char* xsrce_file, char* subDirectory)
{
  MSGS err;
  char *ptr; 
  char strg[bufsize];
  char xlist_file[lname];
  OBJ obj;
  C3Xmode = 1; // Turn off C40 assembly
  g_pass = 0;
  if_on = 1;
  current_seg = 0;
  
  BuildFastLook();  // 12/10/96
  if( err_limit> 32)  err_limit= 32;
  if(warn_limit>128) warn_limit=128;

  
  // Create the filenames used by the assembler

  //strncpy(xsrce_file,_argv[1],lname);
  //if((ptr= strstr(xsrce_file,".")) == NULL) strcat(xsrce_file,ASMEXT);
  //strupr(xsrce_file);

  // Open list file
  strcpy(xlist_file,xsrce_file);
  strlwr((unsigned char*)xlist_file);
  ptr = strstr(xlist_file,".");
  strcpy(ptr+1,"inthex");
  //chi if((dsk_file = fopen(xlist_file,"wt"))==NULL)
  if((hex_file = fopen(xlist_file,"wt"))==NULL)
  { 
    sprintf(strg,">>>> FATAL ERROR: >> CANNOT OPEN %s\r\n",xlist_file);
    fprintf(hex_file,strg);
    //chi fcloseall();
    C30SimCloseAllAssemblerFiles(); 
    fprintf(stderr,
            "error in reading an input file; see .inthex file for detail\n");
    fflush(stderr);
    return FATAL;
  }
  else
    C30SimRegisterAssemblerFile(hex_file);

  //chi - imitate Keith's .hex format
  sprintf(strg,"FILE2HEX\n");
  fprintf(hex_file,strg);

  //  Assemble source file, creating object and temporary file
  SYM[0].numt  = NOREF;
  for(int pass=1;pass<3;pass++)
  {
    init_segments();
    if(pass==1)
    {
    sprintf(strg,"0x00000000  Dummy Bus Width\n");
    fprintf(hex_file,strg);
      assm("pass1 .sdef 1","",obj);
    sprintf(strg,"0x00000000  Dummy Bus Control\n");
    fprintf(hex_file,strg);
      assm("pass2 .sdef 0","",obj);
    }
    if(pass==2)
    {
    // chi - create .hex header format 
      sprintf(strg,"0x00000001  Dummy Section Size\n");
      fprintf(hex_file,strg);
      sprintf(strg,"0x%08lx  Dummy Start Address\n",ENTRY);
      fprintf(hex_file,strg);
      sprintf(strg,"0x00000000  Dummy Data\n");
      fprintf(hex_file,strg);
      
      remove_defines();
      assm("pass1 .sdef 0","",obj);
      assm("pass2 .sdef 1","",obj);
    }
    inc_lvl=0;
    err = passx(xsrce_file,pass,err_limit,warn_limit,subDirectory);

    if(err_cnt && (pass==1))
    {
      //chi fcloseall();
      C30SimCloseAllAssemblerFiles(); 
      if (err) return FATAL;
    }
    // anihilate directive control variables created by .xset
    anihilate_xset();
    switch(err)
    {
      case d_end  : break;
      case NO_LINE: break;
      case NO_ERR : break;
      case OPEN_ERR:
      case FATAL:
      default:
      {
        //chi fcloseall();
        C30SimCloseAllAssemblerFiles(); 
        return FATAL;
      }
    }
  }
  //chi imitate Keith's .hex format
  sprintf(strg,"0x00000000  Terminate Load\n");
  fprintf(hex_file,strg);

  //chi - copy .inthex to .hex with correct "Section Size" information
  FILE * finalhex_file;
  char finalxlist_file[lname];
  strcpy(finalxlist_file,xlist_file);
  ptr = strstr(finalxlist_file,".");
  strcpy(ptr+1,"hex");
  if((finalhex_file = fopen(finalxlist_file,"wt"))==NULL)
  { 
    sprintf(strg,">>>> FATAL ERROR: >> CANNOT OPEN to write%s\r\n",finalxlist_file);
    fprintf(finalhex_file,strg);
    //chi fcloseall();
    C30SimCloseAllAssemblerFiles();
    fprintf(stderr,
            "error in writing to an output file; see .hex file for detail.\n");
    fflush(stderr);
    return FATAL;
  }
  else
    C30SimRegisterAssemblerFile(finalhex_file);

  fclose(hex_file);
  C30SimUnregisterAssemblerFile(hex_file);
  hex_file = fopen(xlist_file,"rt");
  C30SimRegisterAssemblerFile(hex_file);
  int x=1;
  for(x=1; x<last_segment;x++) {
    if(x==1) {
      for(int j=0; j<6; j++) {
        fgets(strg,bufsize-1,hex_file);
        if(strg[0] != '\n') 
          fprintf(finalhex_file,strg);
        else
          j--;
      }
    }
    //move cursor over the dummy section size line
    fgets(strg,bufsize-1,hex_file);
    //copy over opcodes of one section 
    SEG[x].length = SEG[x].offs - SEG[x].start;
    if(SEG[x].length) {
      sprintf(strg,"0x%08lx  Section Size\n", SEG[x].length);
      fprintf(finalhex_file,strg);
      for(int j=0; j<=SEG[x].length; j++) {
        fgets(strg,bufsize-1,hex_file);
        fprintf(finalhex_file,strg);
      } 
    }
    else
      fgets(strg,bufsize-1,hex_file);
  }

  //copy the Terminate Load line
  fgets(strg,bufsize-1,hex_file);
  fprintf(finalhex_file,strg);
  
  //chi reset the reference table
  for(x=0; x<last_ref; x++) 
    *SYM[x].sptr = 0;

  
  strcpy(g_strg1,nullstring);
  strcpy(g_strg2,nullstring);
  g_line = 1;

  if(W_GLBL_FLAG)     {err_cnt--; assm_error_msg(W_GLBL,"");}
  C30SimCloseAllAssemblerFiles(); 
  fclose(finalhex_file);
  return NO_ERR;
}
