#include <stdio.h>
#include <stdlib.h>

#ifdef USE_NLLIBC
#include <nllibc.h>
#endif

#include "config.h"
#include "lib.h"
#include "asm_format.h"

void asm_format_comment(FILE *out, char *comment, char *param1, char *param2, char *param3)
{
  if (param1 == NULL)
    fprintf(out, "\t/* %s */\n", comment);
  else if (param2 == NULL)
    fprintf(out, "\t/* %s (%s) */\n", comment, param1);
  else if (param3 == NULL)
    fprintf(out, "\t/* %s (%s, %s) */\n", comment, param1, param2);
  else
    fprintf(out, "\t/* %s (%s, %s, %s) */\n", comment, param1, param2, param3);
}

void asm_format_newline(FILE *out)
{
  fprintf(out, "\n");
}

void asm_format_align(FILE *out, int size)
{
  /*
   * In GAS, it varies according to the architecture whether .align N is
   * aligned in N or 2^N.
   * This is due to the emulation of the native assembler for the system.
   * (See as.info in binutils)
   * So use .balign N aligned in N bytes. (This is specific to GAS).
   */
#define OP_ALIGN ".balign"
  fprintf(out, "\t%s\t%d\n", OP_ALIGN, size);
}

void asm_format_space(FILE *out, int size)
{
  fprintf(out, "\t.space\t%d, 0\n", size);
}

void asm_format_label(FILE *out, char *label)
{
  fprintf(out, "%s:\n", label);
}

void asm_format_globl(FILE *out, char *name)
{
  fprintf(out, "\t.globl\t%s\n", name);
}

void asm_format_type(FILE *out, char *name, char *prefix, asm_format_type_t type)
{
  char *s = "unknown";

  switch (type) {
  case ASM_FORMAT_TYPE_TYPE_OBJECT:   s = "object";   break;
  case ASM_FORMAT_TYPE_TYPE_FUNCTION: s = "function"; break;
  default: break;
  }

  fprintf(out, "\t.type\t%s, %s%s\n", name, prefix, s);
}

void asm_format_size(FILE *out, char *name, int size)
{
  fprintf(out, "\t.size\t%s, %d\n", name, size);
}

void asm_format_dummy(FILE *out)
{
  fprintf(out, "\t.int\t0\t/* dummy */\n");
}

void asm_format_string(FILE *out, char *string, int size)
{
  int i;

  fprintf(out, "\t.string\t\"");
  for (i = 0; i < size; i++) {
    switch (string[i]) {
    case '\0': fprintf(out, "\\0"); break;
    case '\t': fprintf(out, "\\t"); break;
    case '\n': fprintf(out, "\\n"); break;
    case '\v': fprintf(out, "\\v"); break;
    case '\f': fprintf(out, "\\f"); break;
    case '\r': fprintf(out, "\\r"); break;
    case '\a': fprintf(out, "\\a"); break;
    case '\b': fprintf(out, "\\b"); break;
#if 0 /* Unavailable for clang (Cannot put backslash single quote in string) */
    case '\'': fprintf(out, "\\\'"); break;
#endif
    case '\"': fprintf(out, "\\\""); break;
    case '\\': fprintf(out, "\\\\"); break;
    default:   fprintf(out, "%c", string[i]); break;
    }
  }
  fprintf(out, "\"\n");
}

void asm_format_value(FILE *out, asm_format_type_t type, long number, char *name)
{
  char *s = "unknown";

  switch (type) {
  case ASM_FORMAT_TYPE_VALUE_BYTE:  s = ".byte";  break;
  case ASM_FORMAT_TYPE_VALUE_SHORT: s = ".short"; break;
  case ASM_FORMAT_TYPE_VALUE_INT:   s = ".int";   break;
  case ASM_FORMAT_TYPE_VALUE_QUAD:  s = ".quad";  break;
  default:
    ERREXIT(1);
  }

  if (name)
    fprintf(out, "\t%s\t%s\n", s, name);
  else
    fprintf(out, "\t%s\t%ld\n", s, number);
}

static asm_format_type_t section_type = ASM_FORMAT_TYPE_NONE;

void asm_format_section(FILE *out, asm_format_type_t type)
{
  if (section_type != type) {
    fprintf(out, "\t.section\t");
    switch (type) {
    case ASM_FORMAT_TYPE_SECTION_TEXT: fprintf(out, ".text"); break;
    case ASM_FORMAT_TYPE_SECTION_DATA: fprintf(out, ".data"); break;
    default:
      fprintf(out, "unknown");
      break;
    }
    fprintf(out, "\n\n");
  }

  section_type = type;
}
