#include "vasm.h"

#ifdef OUTPAP

static char *copyright = "vasm MOS paper tape output module 0.2 (c) 2024 Dimitri Theulings";

static uint8_t *buffer;       /* output buffer for data records */
static uint8_t buffer_s = 24; /* default buffer size */
static uint8_t buffer_i = 0;  /* current index in buffer */

static uint32_t addr = 0;     /* current output address */
static uint32_t lines = 0;    /* number of lines in output file */

static int strict = 0;	      /* default not strict */

static void write_newline(FILE *f)
{
  uint8_t i;

  fw8(f,'\r');
  fw8(f,'\n');

  if(strict) /* pad file  with 6 NULL chars */
    for (i=0; i<6; i++)
      fw8(f,0);

  lines++;
}

static void write_eof_record(FILE *f)
{
  uint16_t csum = (lines>>8 & 0xFF) + (lines & 0xFF);

  fprintf(f, ";00%04X%04X", lines, csum);
  write_newline(f);

  if(strict) /* end file with XOFF */
      fw8(f,19);
}

static void write_data_record(FILE *f)
{
  uint32_t csum;
  uint8_t i;
  uint32_t start;

  /* pre-flight checks */
  if (buffer_i == 0)
    return;
  /* addr points to the next inserted byte
     addr-1 is the last one written */
  if (addr-1 > UINT16_MAX)
    output_error(11, addr-1);

  start = addr - buffer_i;
  start &= 0xFFFF;

  /* write data record */
  fprintf(f, ";%02X%04X", buffer_i, start);
  csum = buffer_i + (start>>8 & 0xFF) + (start &0xFF);
  for (i = 0; i < buffer_i; i++) {
    csum = (csum + buffer[i]) & 0xFFFF;
    fprintf(f, "%02X", buffer[i]);
  }
  fprintf(f, "%04X", csum);
  write_newline(f);

  /* reset the buffer index */
  buffer_i = 0;
}

static void buffer_data(FILE *f, uint8_t b)
{
  buffer[buffer_i] = b;
  buffer_i++;
  addr++;

  if (buffer_i == buffer_s)
    write_data_record(f);
}

static void write_output(FILE *f, section *sec, symbol *sym)
{
  uint32_t i, j;
  section *s;
  atom *a;

  if (!sec)
    return;

  for (; sym; sym = sym->next)
    if (sym->type == IMPORT)
      output_error(6, sym->name); /* undefined symbol (sym->name) */

  chk_sec_overlap(sec); /* fail on overlapping sections */

  buffer = mymalloc(sizeof(uint8_t) * buffer_s);

  for (s = sec; s; s = s->next) {
    addr = (utaddr)s->org;
    for (a = s->first; a!=s->end; a = a->next) {
      if (a->type == DATA) {
        for (i = 0; i < a->content.db->size; i++) {
          buffer_data(f, a->content.db->data[i]);
        }
      } else if (a->type == SPACE) {
        for (i = 0; i < a->content.sb->space; i++) {
          for (j = 0; j < a->content.sb->size; j++) {
            buffer_data(f, a->content.sb->fill[j]);
          }
        }
      }
    }
    /* flush buffer before moving on to next section */
    write_data_record(f);
  }

  write_eof_record(f);
  myfree(buffer);
}

static int parse_args(char *arg)
{
  int size;
  long long val;

  if (!strcmp(arg, "-strict")) {
    strict = 1;
    return 1;
  }
  else if (!strncmp(arg, "-record-size=", 13)) {
    size = atoi(arg + 13);
    /* a PAP record (incl. header) cannot be longer than 0xFF bytes */
    if (size < 1 || size > 249)
      return 0;
    buffer_s = size;
    return 1;
  }
  else if (!strncmp(arg,"-start=",7)) {
    sscanf(arg+7,"%lli",&val);
    defsectorg = val;  /* set start of default section */
    return 1;
  }
  return 0;
}

int init_output_pap(char **cp, void (**wo)(FILE *, section *, symbol *), int (**oa)(char *))
{
  if (sizeof(utaddr) > sizeof(uint32_t)) {
    output_error(1, cpuname); /* output module doesn't support (cpuname) */
    return 0;
  }

  *cp = copyright;
  *wo = write_output;
  *oa = parse_args;
  defsecttype = emptystr;  /* default section is "org 0" */
  return 1;
}

#else
int init_output_pap(char **cp, void (**wo)(FILE *, section *, symbol *), int (**oa)(char *))
{
  return 0;
}
#endif
