/*hdr.c - output an assembler header file given a module defn*/

/*OSLib---efficient, type-safe, transparent, extensible,\n"
   register-safe A P I coverage of RISC O S*/
/*Copyright  1994 Jonathan Coxhead*/

/*
      OSLib is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 1, or (at your option)
   any later version.

      OSLib is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
   along with this programme; if not, write to the Free Software
   Foundation, Inc, 675 Mass Ave, Cambridge, MA 02139, U S A.
*/

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "kernel.h"      /* TV 980115 */

#include "oslib/macros.h"
#include "oslib/os.h"
#include "oslib/types.h"

#include "lookup.h"

#include "def.h"
#include "hdr.h"

#define TAB 8
#define LABELLED_INST_COL 4
#define LABELLED_ARG_COL 5

#define UNLABELLED_INST_COL 1
#define UNLABELLED_ARG_COL  2

static char *Op [] = {":OR:", ":AND:", "+", ":EOR:"};

static int Emit
(
   FILE *file,
   char *label,
   char *inst
)
{
   return label != NULL?
         fprintf (file, "%-*s %s\n", LABELLED_INST_COL*TAB   - 1, label, inst):
         fprintf (file, "%-*s %s\n", UNLABELLED_INST_COL*TAB - 1, "",    inst);
}

static int EmitS
(
   FILE *file,
   char *label,
   char *inst,
   char *s
)
{
   return label != NULL?
         fprintf (file, "%-*s %-*s %s\n", LABELLED_INST_COL*TAB - 1, label,
               (LABELLED_ARG_COL - LABELLED_INST_COL)*TAB - 1, inst, s):
         fprintf (file, "%-*s %-*s %s\n", UNLABELLED_INST_COL*TAB - 1,
               "", (UNLABELLED_ARG_COL - UNLABELLED_INST_COL)*TAB - 1,
               inst, s);
}

static int EmitSS
(
   FILE *file,
   char *label,
   char *inst,
   char *s1,
   char *s2
)
{
   return label != NULL?
         fprintf (file, "%-*s %-*s %s%s\n", LABELLED_INST_COL*TAB - 1,
               label, (LABELLED_ARG_COL - LABELLED_INST_COL)*TAB - 1, inst,
               s1, s2):
         fprintf (file, "%-*s %-*s %s%s\n", UNLABELLED_INST_COL*TAB - 1,
               "", (UNLABELLED_ARG_COL - UNLABELLED_INST_COL)*TAB - 1,
               inst, s1, s2);
}

static int EmitSSS
(
   FILE *file,
   char *label,
   char *inst,
   char *s1,
   char *s2,
   char *s3
)
{
   return label != NULL?
         fprintf (file, "%-*s %-*s %s%s%s\n", LABELLED_INST_COL*TAB - 1,
               label, (LABELLED_ARG_COL - LABELLED_INST_COL)*TAB - 1, inst,
               s1, s2, s3):
         fprintf (file, "%-*s %-*s %s%s%s\n", UNLABELLED_INST_COL*TAB - 1,
               "", (UNLABELLED_ARG_COL - UNLABELLED_INST_COL)*TAB - 1,
               inst, s1, s2, s3);
}

static int EmitD
(
   FILE *file,
   char *label,
   char *inst,
   int d
)
{
   return label != NULL?
         fprintf (file, "%-*s %-*s %d\n", LABELLED_INST_COL*TAB - 1, label,
               (LABELLED_ARG_COL - LABELLED_INST_COL)*TAB - 1, inst, d):
         fprintf (file, "%-*s %-*s %d\n", UNLABELLED_INST_COL*TAB - 1,
               "", (UNLABELLED_ARG_COL - UNLABELLED_INST_COL)*TAB - 1,
               inst, d);
}

static int EmitX
(
   FILE *file,
   char *label,
   char *inst,
   int x
)
{
   return label != NULL?
         fprintf (file, "%-*s %-*s &%X\n", LABELLED_INST_COL*TAB - 1, label,
               (LABELLED_ARG_COL - LABELLED_INST_COL)*TAB - 1, inst, x):
         fprintf (file, "%-*s %-*s &%X\n", UNLABELLED_INST_COL*TAB - 1,
               "", (UNLABELLED_ARG_COL - UNLABELLED_INST_COL)*TAB - 1,
               inst, x);
}

/* Prints a declaration of |v| as an object of type |t|.*/

static int Emit_Decl
(
   FILE  *file,
   def_t  t,
   char  *v,
   osbool top_level,
   int    count
)
{
   int  rc = 0;
   char cs [1 + DEC_WIDTH + 1];

   if (count == 0)
      CLEAR (cs);
   else
      sprintf (cs, "%d*", count);

   switch (t->tag)
   {
      case def_TYPE_INT:
         if ((rc = EmitSS (file, v, top_level? "*": "#", cs, "Int")) < 0)
            goto finish;
      break;

      case def_TYPE_SHORT:
         if ((rc = EmitSS (file, v, top_level? "*": "#", cs, "Short")) < 0)
            goto finish;
      break;

      case def_TYPE_BYTE:
         if ((rc = EmitSS (file, v, top_level? "*": "#", cs, "Byte")) < 0)
            goto finish;
      break;

      case def_TYPE_CHAR:
         if ((rc = EmitSS (file, v, top_level? "*": "#", cs, "Char")) < 0)
            goto finish;
      break;

      case def_TYPE_BITS:
         if ((rc = EmitSS (file, v, top_level? "*": "#", cs, "Bits")) < 0)
            goto finish;
      break;

      case def_TYPE_BYTES:
         if ((rc = EmitSS (file, v, top_level? "*": "#", cs, "Bytes")) < 0)
            goto finish;
      break;

      case def_TYPE_BOOL:
         if ((rc = EmitSS (file, v, top_level? "*": "#", cs, "Bool")) < 0)
            goto finish;
      break;

      case def_TYPE_REF:
         if ((rc = EmitSS (file, v, top_level? "*": "#", cs, "Ptr")) < 0)
            goto finish;
      break;

      case def_TYPE_STRING: case def_TYPE_ASM: case def_TYPE_DATA: case
            def_TYPE_VOID:
         if ((rc = EmitS (file, v, top_level? "*": "#", "Skip")) < 0)
            goto finish;
      break;

      case def_TYPE_STRUCT:
      {
         int i;

         if (top_level)
            if ((rc = EmitD (file, "", "^", 0)) < 0)
               goto finish;

         for (i = 0; i < t->data AS list.count; i++)
         {
            char v1 [def_ID_LIMIT + 1];

            if ((rc = sprintf (v1, "%s_%s", v, t->data AS
                  list.members [i]->name)) < 0)
               goto finish;

            if ((rc = Emit_Decl (file, t->data AS list.members [i], v1,
                  FALSE, 0)) < 0)
               goto finish;
         }

         if (top_level)
            if ((rc = EmitS (file, v, "*", "@")) < 0)
               goto finish;
      }
      break;

      case def_TYPE_UNION:
      {
         int i;

         if (top_level)
            if ((rc = EmitD (file, "", "^", 0)) < 0)
               goto finish;

         /*Use the union name itself to hold the offset of the start of the
            union.*/
         if (!top_level)
            if ((rc = EmitS (file, v, "*", "@")) < 0)
               goto finish;

         for (i = 0; i < t->data AS list.count; i++)
         {
            char v1 [def_ID_LIMIT + 1];

            if (i > 0)
            {
               /*Set {VAR} back to the origin of the union.*/
               if (!top_level)
               {
                  if ((rc = EmitS (file, "", "^", v)) < 0)
                     goto finish;
               }
               else
               {
                  if ((rc = EmitD (file, "", "^", 0)) < 0)
                     goto finish;
               }
            }

            if ((rc = sprintf (v1, "%s_%s", v, t->data AS
                  list.members [i]->name)) < 0)
               goto finish;

            if ((rc = Emit_Decl (file, t->data AS list.members [i], v1,
                  FALSE, 0)) < 0)
               goto finish;
         }

         if (top_level)
            if ((rc = EmitS (file, v, "*", "@")) < 0)
               goto finish;
      }
      break;

      case def_TYPE_ROW:
         if ((rc = Emit_Decl (file, t->data AS row.base, v,
               FALSE, (count == 0? 1: count)*t->data AS row.count)) < 0)
            goto finish;
      break;

      case def_TYPE_ID:
         if ((rc = EmitS (file, v, top_level? "*": "#", t->data AS id)) < 0)
            goto finish;
      break;
   }

   if (top_level)
      if ((rc = fprintf (file, "\n")) < 0)
         goto finish;

finish:
   return rc;
}

/* Prints a description of an object of type |t|.*/

static int Emit_Name
(
   FILE  *file,
   def_t  t
)
{
   int rc = 0;

   switch (t->tag)
   {
      case def_TYPE_INT:
         if ((rc = fprintf (file, "Int")) < 0)
            goto finish;
      break;

      case def_TYPE_SHORT:
         if ((rc = fprintf (file, "Short")) < 0)
            goto finish;
      break;

      case def_TYPE_BYTE:
         if ((rc = fprintf (file, "Byte")) < 0)
            goto finish;
      break;

      case def_TYPE_CHAR:
         if ((rc = fprintf (file, "Char")) < 0)
            goto finish;
      break;

      case def_TYPE_BITS:
         if ((rc = fprintf (file, "Bits")) < 0)
            goto finish;
      break;

      case def_TYPE_BYTES:
         if ((rc = fprintf (file, "Bytes")) < 0)
            goto finish;
      break;

      case def_TYPE_BOOL:
         if ((rc = fprintf (file, "Bool")) < 0)
            goto finish;
      break;

      case def_TYPE_REF:
         if ((rc = fprintf (file, "pointer to ")) < 0)
            goto finish;

         if ((rc = Emit_Name (file, t->data AS ref)) < 0)
            goto finish;
      break;

      case def_TYPE_STRING:
         if ((rc = fprintf (file, "String")) < 0)
            goto finish;
      break;

      case def_TYPE_ASM:
         if ((rc = fprintf (file, "code")) < 0)
            goto finish;
      break;

      case def_TYPE_DATA:
         if ((rc = fprintf (file, "data")) < 0)
            goto finish;
      break;

      case def_TYPE_VOID:
         if ((rc = fprintf (file, "unspecified")) < 0)
            goto finish;
      break;

      case def_TYPE_STRUCT:
      case def_TYPE_UNION:
      {
         int i;

         if ((rc = fprintf (file, "%s of (",
               t->tag == def_TYPE_STRUCT? "sequence": "one")) < 0)
            goto finish;

         for (i = 0; i < t->data AS list.count; i++)
         {
            if (i > 0)
               if ((rc = fprintf (file, ", ")) < 0)
                  goto finish;

            if ((rc = Emit_Name (file, t->data AS list.members [i])) < 0)
               goto finish;
         }

         if ((rc = fprintf (file, ")")) < 0)
            goto finish;
      }
      break;

      case def_TYPE_ROW:
         if ((rc = fprintf (file, "array of %d ", t->data AS row.count)) < 0)
            goto finish;

         if ((rc = Emit_Name (file, t->data AS row.base)) < 0)
            goto finish;
      break;

      case def_TYPE_ID:
         if ((rc = fprintf (file, "%s", t->data AS id)) < 0)
            goto finish;
      break;
   }

finish:
   return rc;
}

static int Emit_Get (FILE *file, char *name)

{  int  rc;
   char s [FILENAME_MAX + 1], get [FILENAME_MAX + 1],
              hdr [FILENAME_MAX + 1];

   if ((rc = sprintf (get, "%s%s", "Get_", name)) < 0)
      goto finish;
   if ((rc = sprintf (hdr, "%s%s", name, "_Hdr")) < 0)
      goto finish;

   if ((rc = EmitSS (file, NULL, "[", ":LNOT: :DEF: ", get)) < 0)
      goto finish;
   if ((rc = EmitS (file, NULL, "GBLS", get)) < 0)
      goto finish;
   if ((rc = Emit (file, NULL, "]")) < 0)
      goto finish;

#if 0    // Amended to reflect change of include path name *** TV 20000305 ***
   if ((rc = sprintf (s, "\"GET OS:Hdr.%s\"", name)) < 0)
#elif 0 // And again 20000930
   if ((rc = sprintf (s, "\"GET OSLib:Hdr.%s\"", name)) < 0)
#else
#ifdef EXECUTE_ON_UNIX
   if ((rc = sprintf (s, "\"GET oslib/Hdr/%s\"", name)) < 0)
#else
   if ((rc = sprintf (s, "\"GET OSLib:oslib.Hdr.%s\"", name)) < 0)
#endif
#endif
      goto finish;

   if ((rc = EmitSS (file, NULL, "[", ":LNOT: :DEF: ", hdr)) < 0)
      goto finish;
   if ((rc = EmitS (file, get, "SETS", s)) < 0)
      goto finish;
   if ((rc = Emit (file, NULL, "|")) < 0)
      goto finish;
   if ((rc = EmitS (file, get, "SETS", "\"\"")) < 0)
      goto finish;
   if ((rc = Emit (file, NULL, "]")) < 0)
      goto finish;

   if ((rc = sprintf (s, "$Get_%s", name)) < 0)
      return rc;
   if ((rc = Emit (file, NULL, s)) < 0)
      goto finish;

   if ((rc = fprintf (file, "\n")) < 0)
      goto finish;

finish:
   return rc;
}

os_error *hdr_output
(
   FILE     *file,
   char     *title,
   char     *author,
   lookup_t  needses,
   lookup_t  consts,
   lookup_t  types,
   lookup_t  swis
)
{
   os_error *error = NULL;
   char     *needs, *cnst, *type, *swi;
   def_c     c;
   def_t     t;
   def_s     s;
   void     *context;
   int       rc = 0;
   time_t    now;
   osbool    start;

   /*Emit the TITLE declaration.*/
   if ((rc = fprintf (file,
         ";Assembler header file for %s\n"
         ";written by DefMod (%s) on %s"
         ";%s\n"
         "\n",
         title, __DATE__, (time (&now), ctime (&now)), author)) < 0)
      goto finish;

   /*Make sure we have Hdr.Types.*/
   if ((rc = Emit_Get (file, "Types")) < 0)
      goto finish;

   /*Emit the NEEDS declarations.*/
   context = 0;
   while (TRUE)
   {
      if ((error = lookup_enumerate (needses, &needs, NULL, &context)) !=
            NULL)
         goto finish;
      if (context == 0) break;

      if ((rc = Emit_Get (file, needs)) < 0)
         goto finish;
   }

   if ((rc = EmitSSS (file, NULL, "[", ":LNOT: :DEF: ", title, "_Hdr")) < 0)
      goto finish;
   if ((rc = EmitSS (file, NULL, "GBLS", title, "_Hdr")) < 0)
      goto finish;
   if ((rc = fprintf (file, "\n")) < 0)
      goto finish;

   /*Emit the CONST declarations.*/
   start = TRUE;
   context = 0;
   while (TRUE)
   {
      if ((error = lookup_enumerate (consts, &cnst, (void **) &c, &context))
            != NULL)
         goto finish;
      if (context == 0) break;

      if (start)
      {
         if ((rc = fprintf (file, ";Symbols for constants\n")) < 0)
            goto finish;
         start = FALSE;
      }

      switch (c->type->tag)
      {
         case def_TYPE_INT: case def_TYPE_SHORT: case def_TYPE_BYTE:
            if ((rc = EmitD (file, cnst, "*", c->value)) < 0)
               goto finish;
         break;

         case def_TYPE_CHAR:
         {
            char v1 [def_ID_LIMIT + 1];

            if (isprint (c->value))
            {
               if ((rc = sprintf (v1, "\"%c\"", c->value)) < 0)
                  goto finish;
            }
            else
            {
               if ((rc = sprintf (v1, ":CHR: &%.2X", c->value)) < 0)
                  goto finish;
            }
            if ((rc = EmitS (file, cnst, "*", v1)) < 0)
               goto finish;
         }
         break;

         case def_TYPE_BOOL:
         {
            char v1 [def_ID_LIMIT + 1];

            if ((rc = sprintf (v1, "{%s}", WHETHER (c->value))) < 0)
               goto finish;
            if ((rc = EmitS (file, cnst, "*", v1)) < 0)
               goto finish;
         }
         break;

         default:
            if ((rc = EmitX (file, cnst, "*", c->value)) < 0)
               goto finish;
         break;
      }
   }

   if (!start)
      if ((rc = fprintf (file, "\n")) < 0)
         goto finish;

   /*Emit the TYPE declarations.*/
   start = TRUE;
   context = 0;
   while (TRUE)
   {
      if ((error = lookup_enumerate (types, &type, (void **) &t, &context))
            != NULL)
         goto finish;
      if (context == 0) break;

      if (start)
      {
         if ((rc = fprintf (file,
               ";Symbols for structure offsets and sizes\n")) < 0)
            goto finish;
         start = FALSE;
      }

      if (t != NULL && t->tag != def_TYPE_ABSTRACT)
      {
         if ((rc = Emit_Decl (file, t, type, TRUE, 0)) < 0)
            goto finish;
      }
      else
      {
         if ((rc = EmitS (file, type, "*", "4 ;abstract type")) < 0)
            goto finish;
      }
   }

   if (!start)
      if ((rc = fprintf (file, "\n")) < 0)
         goto finish;

   /*Emit the SWI definitions.*/
   start = TRUE;
   context = 0;
   while (TRUE)
   {
      char title [def_ID_LIMIT + 1] = "X";
      osbool first, titled;
      int  i;

      if ((error = lookup_enumerate (swis, &swi, (void **) &s,
            &context)) != NULL)
         goto finish;
      if (context == 0) break;

      if (start)
      {
         if ((rc = fprintf (file,
               ";Symbols for SWI's and SWI reason codes\n")) < 0)
            goto finish;
         start = FALSE;
      }

      strcat (title, swi);

      titled = FALSE;
      if (s->starred_swi)
      {
         if ((rc = EmitX (file, title, "*", s->swi | 1 << 17)) < 0)
            goto finish;
         if ((rc = EmitX (file, title + 1, "*", s->swi)) < 0)
            goto finish;
         titled = TRUE;
      }
      else
         for (i = 0; i < 10; i++)
            if (s->starred_constants [i])
            {  if ((rc = EmitX (file, title + 1, "*", s->constants [i])) < 0)
                  goto finish;
               titled = TRUE;
               break;
            }

      if (titled)
      {
         if (!s->absent)
         {
            first = TRUE;
            for (i = 0; i < 10; i++)
               if ((s->k & 1 << i) != NONE)
               {
                  if (first)
                  {
                     if ((rc = fprintf (file, "   ;Entry\n")) < 0)
                        goto finish;
                     first = FALSE;
                  }

                  if ((s->i & 1 << i) != NONE)
                  {
                     /*there is an operation to involve*/
                     if ((rc = fprintf (file,
                           "   ;  R%d = &%X %s %s (",
                           i, s->constants [i], Op [s->op [i]],
                           s->inputs [i]->name)) < 0)
                        goto finish;

                     if ((rc = Emit_Name (file, s->inputs [i])) < 0)
                        goto finish;

                     if ((rc = fprintf (file, ")\n")) < 0)
                        goto finish;
                  }
                  else
                  {
                     if ((rc = fprintf (file,
                           "   ;  R%d = &%X\n",
                           i, s->constants [i])) < 0)
                        goto finish;
                  }
               }
               else if ((s->i & 1 << i) != NONE)
               {
                  if (first)
                  {
                     if ((rc = fprintf (file, "   ;Entry\n")) < 0)
                        goto finish;
                     first = FALSE;
                  }

                  if ((rc = fprintf (file, "   ;  R%d %s %s (",
                        i,
                        (s->ri & 1 << i) == 0? "=": "->",
                        s->inputs [i]->name)) < 0)
                     goto finish;

                  if ((rc = Emit_Name (file, s->inputs [i])) < 0)
                     goto finish;

                  if ((rc = fprintf (file, ")\n")) < 0)
                     goto finish;
               }

            first = TRUE;
            for (i = 0; i < 10; i++)
               if ((s->o & 1 << i) != NONE)
               {
                  if (first)
                  {
                     if ((rc = fprintf (file, "   ;Exit\n")) < 0)
                        goto finish;
                     first = FALSE;
                  }

                  if ((rc = fprintf (file,
                        "   ;  R%d %s %s (",
                        i,
                        (s->ro & 1 << i) == 0? "=": "->",
                        s->outputs [i]->name)) < 0)
                     goto finish;

                  if ((rc = Emit_Name (file, s->outputs [i])) < 0)
                     goto finish;

                  if ((rc = fprintf (file, ")\n")) < 0)
                     goto finish;
               }
               else if ((s->c & 1 << i) != NONE)
               {
                  if (first)
                  {
                     if ((rc = fprintf (file, "   ;Exit\n")) < 0)
                        goto finish;
                     first = FALSE;
                  }

                  if ((rc = fprintf (file,
                        "   ;  R%d corrupted\n", i)) < 0)
                     goto finish;
               }
         }

         if ((rc = fprintf (file, "\n")) < 0)
            goto finish;
      }
   }

   if ((rc = Emit (file, NULL, "]")) < 0)
      goto finish;
   if ((rc = Emit (file, NULL, "END")) < 0)
      goto finish;

finish:
   if (rc < 0) error = (os_error *)_kernel_last_oserror ();      /* TV 980115 */
   return error;
}
