/* ---------------------------------------------------------------------- */
/*                   Copyright (C) 1992 by Natrlich!                     */
/*                      This file is copyrighted!                         */
/*                Refer to the documentation for details.                 */
/* ---------------------------------------------------------------------- */
/* rev. history   1.0 --                                      */
/*                1.1 --                                      */
/*                1.2 -- removed NIL crashes on faulty MAC65  */
/*                1.3 -- -c option searches directory         */
/*                1.4 -- faulty M65 files are treated kinder  */
/*                1.5 -- no real changes                      */
/* ---------------------------------------------------------- */

#include "defines.h"
#if __NSTDC__ && __TURBOC__
#pragma warn -pia   /* Don't want those if( foo = bar) warnings */
#endif

#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <setjmp.h>
#define __BIG_GENERATOR__
#include OSBIND
#include "code.h"

#define COMMENTPAD  24
#define MACROPAD    06
#define INSTRPAD    06
#define DATAPAD     12

#if OS == TOS
struct    
{
    char    reserved[ 21];
    byte    attribs;
    word    time,
            date;
    lword   len;
    char    name[ 14];
} dir;
#endif

void  convert(),
      nflush(),
      io(),
      label(),
      nput(),
      nprint(),
      comment(),
      padto();

struct
{
   word  fefe,
         len;
} header;

#ifdef NIL
#undef NIL
#endif
#define NIL (char *) 0

#define TAB1SIZE 95

char  *table1[] =
{
   "error -",  ".if",      ".else",    ".endif",   ".macro",
   ".endm",    ".title",   NIL,        ".page",    ".word",
   ".error",   ".byte",    ".sbyte",   ".dbyte",   ".end",
   ".opt",     ".tab",     ".include", ".ds",      ".org",
   ".equ",     "bra",      "trb",      "tsb",      ".float",
   ".cbyte",   "",         ".local",   ".set",     "*=",
   "=",        ".=",       "jsr",      "jmp",      "dec",
   "inc",      "ldx",      "ldy",      "stx",      "sty",
   "cpx",      "cpy",      "bit",      "brk",      "clc",
   "cld",      "cli",      "clv",      "dex",      "dey",
   "inx",      "iny",      "nop",      "pha",      "php",
   "pla",      "plp",      "rti",      "rts",      "sec",
   "sed",      "sei",      "tax",      "tay",      "tsx",
   "txa",      "txs",      "tya",      "bcc",      "bcs",
   "beq",      "bmi",      "bne",      "bpl",      "bvc",
   "bvs",      "ora",      "and",      "eor",      "adc",
   "sta",      "lda",      "cmp",      "sbc",      "asl",
   "rol",      "lsr",      "ror",      NIL,        "stz",
   "dea",      "ina",      "phx",      "phy",      "plx",
   "ply"
};


static char    ref[] = ".ref ",  and[] = ".and ", not[]= ".not ",
               or[]  = ".or ",   def[] = ".def ";
               
#define TAB2SIZE  78

char  otable2[] =     /* 0 - 77 */
{
 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 1, 1, 1, 0, 0,
 0, 0, 1, 1, 1, 1, 1, 0,
 1, 1, 1, 1, 1, 1, 1, 1,
 1, 0, 0, 0, 1, 1, 0, 1,
 0, 0, 0, 0, 0, 0, 0, 1,
 1, 1, 1, 1, 1, 1, 1, 1,
 1, 1, 1, 0, 0, 1, 1, 1,
 1, 1, 1, 1, 1, 1, 1, 1,
 1, 1, 1, 1, 1, 1
};

char  *table2[] =     /* 0 - 77 */
{
   NIL,     NIL,     NIL,     NIL,     NIL,     NIL,     NIL,     NIL,
   NIL,     NIL,     NIL,     "%$",    "%",     "*",     " ",     " ",     
   "a",     "q",     "+",     "-",     "*",     "/",     "&",     NIL,     
   "=",     "<=",    ">=",    "<>",    ">",     "<",     "-",     "[",     
   "]",     or,      and,     not,     "!",     "^",     ref,     "\\",    
   NIL,     NIL,     NIL,     NIL,     "Edit",  "What!", "Text:", ref,     
   def,     not,     and,     or,      "<",     ">",     ",x)",   "),y",   
   ",y",    ",x",    ")",     NIL,     NIL,     ",",     "#",     "a",     
   "(",     "\"",    "$",     NIL,     NIL,     "no ",   "obj ",  "err ",  
   "eject ","list ", "xref ", "mlist ","clist ","num "
};


#define tolower( c)  ((c) >= 'A' && (c) <= 'Z'  ? (c) + 32 : (c))

#if OS == TOS
int   tossable;
#endif

char  *currfile = "[none]";
jmp_buf  errjmp;


static char    tiny[8],
               infile[ 256], 
               outfile[256];

word           __x;
byte huge      *p;
long           len;
int            fp,
               forceit,
               delete;
FILE           *fq;



void nferror( s)
char  *s;
{
   fprintf( stderr, "demac65: \"%s\" Fatal error - %s\n", currfile, s);
   exit(1);
}


void nerror( s)
char  *s;
{
   fprintf( stderr, "demac65: \"%s\" Error - %s\n", currfile, s);
   Fclose( fp);
   if( outfile[ 0])
      fclose( fq);
   longjmp( errjmp, 1);
}

unsigned int   lines;

void nwarning( s)
char  *s;
{
   fprintf( stderr, "demac65: \"%s\" ca. line %d, Warning - %s\n", 
               currfile, lines + 1, s);
}

void  complete( filename, ext, force)
char  *filename, *ext;
{
   char  *x;

   if( x = strrchr( filename, '.'))
   {
      if( strchr( x, DIRSLASH) > x)
         goto dis;
      if( force)
         while( *++x = *++ext);
   }
   else
dis:
      strcat( filename, ext);
}


int   chk_ext( filename, ext)
char  *filename, *ext;
{
   char  *x;

   if( x = strrchr( filename, '.'))
      if( strchr( x, DIRSLASH) <= x)
         return( strcmp( x + 1, ext));
   return( 1);
}


int   main( argc, argv)
int    argc;
char   *argv[];
{
   int          i = 0;

   fq = stdout;          /* done here for some bogus Amiga CC */
   while( ++i < argc)
   {
      if( *argv[i] == '-')
         switch( argv[i][1])
         {
#if OS == TOS
            case 'T' :
            case 't' :
               tossable = ! tossable;
               break;
#endif
            case 'D' :
            case 'd' :
               delete  = ! delete;
               break;
               
            case 'C' :
            case 'c' :
               forceit = ! forceit;
               break;
               
#ifdef __DATE__
            case ':' :
               fputs( __DATE__, stderr);
# ifdef __TIME__
               fprintf( stderr, " %s", __TIME__);
# endif                              
               putc( '\n', stderr);
               break;
#endif               
            case 'V' :
            case 'v' :
               fprintf( stderr, "demac65 v1.5 (c) 1992 by Natuerlich! -- detokenizes MAC65 files\n");
               break;
         }
      else
         if( ! infile[0])
            strcpy( infile, argv[i]);
         else
            if( ! outfile[0])
               strcpy( outfile, argv[i]);
            else
               goto usage;
   }
   if( ! infile[0])
      goto usage;

   if( ! setjmp( errjmp))
   {
      if( forceit)
      {
         strcpy( outfile, infile);
         complete( outfile, ".s65", 1);
#if OS == TOS
         if( strchr( infile, '*'))        /* doesn't work well but works */
         {
            Fsetdta( (void *) &dir);
            if( Fsfirst( infile, 0))
               nferror( "No MaTcH");      /* waste user lifetime */
            do
               if( chk_ext( dir.name, "S65"))
               {    
                  strcpy( outfile, dir.name);
                  complete( outfile, ".s65", 1);
                  if( ! setjmp( errjmp))
                     io( dir.name, outfile);
              }
            while( ! Fsnext());
            return( 0);
         }
#endif
      }
      io( infile, outfile);
      return( 0);
   }
   return( 1);

usage:
#if OS == TOS
   fprintf( stderr, "usage: demac65 [-{td}]{-c <filemask>|[-c] <infile> \
[outfile]}\n");
   fputs( 
"\t-c <filemask> : convert 1 or more files, f.e. -c '*.*' converts all\n", 
   stderr);
#else
   fprintf( stderr, "usage: demac65 [-{cd}] <infile> [outfile]\n");
#endif
   
   fputs( "\
\t-c <infile>   : convert <infile> to <infile>.S65\n\
\t-d            : delete after conversion\n\
\t-v            : version\n", stderr);
#if OS == TOS
   fputs( "\t-t            : wait for keypress before exiting\n", stderr);
   keypress();
#endif
   return(1);
}   

void  io( in, out)
char  *in, *out;
{
   static char    buf[256];
   
   currfile = in;
   if( (fp = (int) Fopen( in, 0)) < 0)
   {
      sprintf( buf, "Couldn't open source file \"%s\"", in);
      nferror( buf);
   }
   if( Fread( fp, 4L, &header) < 4)
      nerror("File trunctated");
   if( header.fefe != 0xFEFE)
      nerror("Not a MAC65 token file");
   if( len = dpeek( &header.len))
   {
      if( ! (p = (byte huge *) Malloc( len)))
         nferror("Out of memory");
      if( Fread( fp, len, p) < len)
         nerror("Source file truncated");
      if( out[0] && ! (fq = fopen( out, "w")))
      {
         sprintf( buf, "Couldn't create output file \"%s\"", out);
         nferror( buf);
      }
      convert();
      if( delete && ! unlink( in))
         fprintf( stderr, "Deleted \"%s\"\n", in);
      if( out[0])
         fclose( fq);
   }
   Fclose( fp);
}

int   indent, 
      pad, 
      i, 
      inquotes;
byte  c, d;


void convert()
{
   lines = 1;
   indent = inquotes = 0;
   while( len)
   {
      pad = 0;         /* start from left */
      p  += 2;         /* ignore lineno   */

      i = (word) *p++ - 3;  /* line length */
      len -= 3 + i;     /* minus lineno + linelength + linebyte */

      while( i)
      {
         i--;
         c = *p & 0x7F;
         if( c != *p++)
         {
            label( 0);
            nput( ' ');
            continue;
         }
      
         switch( c)
         {
            case   0 :
               nprint( table1[0]);     /* am i lazy... */
               comment();
               break;
               
            case   7 :
               padto(MACROPAD);         
               c = *p++ & 0x7F;
               i--;
               label(1);
               nput(' ');
               if( xconvert())
                  goto salvage;
               break;

            case  88 :
               comment();
               break;

            default  :
               d = c;
               if( d == 2 || d == 3 || d == 5) /* .ELSE .ENDIF .ENDM*/
                  indent -= 3;
               padto( INSTRPAD);
               if( c >= TAB1SIZE)
               {
                  nerror("Got an totally unknown instr. token");
salvage:
                  p += i;
                  i = 0;
                  break;
               }
               else
                  nprint( table1[ c]);
               nput(' ');
               if( xconvert())
                  goto salvage;
               if( d == 1 || d == 2 || d == 4)
                  indent += 3;
         } 
      }
      nput( '\n');
      lines++;
   }
}

xconvert()
{
   inquotes = 0;
   if( i)
      padto( DATAPAD);
   while( i)
   {
      i--;
      c = *p & 0x7F;
      if( c != *p++)
      {
         label( inquotes);
         continue;
      }
      
      switch( c)
      {
         case  1 :
         case  3 :
         case  5 :
            sprintf( tiny, "$%X", ((word) p[1] << 8) | (word) *p);
            p += 2; 
            i -= 2; 
            nprint( tiny); 
            break;
               
         case  0 :
         case  2 :
         case  4 :
         case  6 :
            sprintf( tiny, "$%X", (word) *p++);
            i--; 
            nprint( tiny); 
            break;
               
         case  7 :
         case  9 :
            sprintf( tiny, "%u", ((word) p[1] << 8) |  *p);
            p += 2; 
            i -= 2; 
            nprint( tiny); 
            break;
            
         case  8 :
            sprintf( tiny, "%u", *p++);
            i--; 
            nprint( tiny); 
            break;
           
         case 10 :
            nput('\''); 
            nput( (int) *p++); 
            i--; 
            break;
   
         case 59 :
            padto( COMMENTPAD);
            if( *p != ';')
               nput( ';');
            comment(); 
            break;
                           
         case 65 :
            inquotes = ! inquotes;
            
         default  :
         {
            if( c >= TAB2SIZE)
            {
               nerror("Got an undetokenizable token");
               return( 1);
            }
            else
            {
               if( ! otable2[ c])
                  nwarning("Unusual token");
               nprint( table2[ c]);
            }
         }
      } 
   }
   return( 0);
}   

/* Vot = 0 lower case */
void label( vot) 
{ 
   char  v;
   int   x = c; 

   i -= c;
   while( x) 
   {
      x--;
      v = *p++;
      nput( (int) (vot ? v : tolower(v))); 
   }
}

void comment()
{
   while( i)
   {
      i--;
      nput( (int) *p++);
   }
}


void padto(to) 
int  to; 
{  
   to += indent;
   while( pad < to) 
      nput(' ');    
}
   

void nput( c)
int  c;
{
   putc( c, fq);
   pad++;
}


void nprint( s)
char  *s;
{
   int   i;
   
   if( s)
   {
      i = (int) strlen( s);
   
      if( fputs( s, fq))
         nferror("Disk full ?");
      pad += i;
   }
   else
      nerror("Got an undetokenizable token");
}

keypress()
{
#if OS == TOS
   if( tossable)
   {
      while( Bconstat( 2))
         Bconin( 2);
      Bconin( 2);
   }
#endif
}

