/* Copyright (c) 1988,1991 by Sozobon, Limited.  Author: Johann Ruegg
 *
 * Permission is granted to anyone to use this software for any purpose
 * on any computer system, and to redistribute it freely, with the
 * following restrictions:
 * 1) No charge may be made other than reasonable charges for reproduction.
 * 2) Modified versions must be clearly marked as such.
 * 3) The authors are not responsible for any harmful consequences
 *    of using this software, even if they result from defects in it.
 *
 * out.c
 *
 * Code generation output routines.
 *
 * Modified by Detlef Wuerkner for AMIGA
 * Changes marked with TETISOFT
 *
 * Previous changes by Joe Montgomery labeled JMM
 *
 * 1993
 * Modified by Jason Petty to work with hce.
 * Changes marked with VANSOFT.
 */


#include <clib/stdio.h>
#include "h_inc/param.h"
#include "h_inc/nodes.h"
#include "h_inc/flags.h"
#include "h_inc/bstok.h"
#include "h_inc/tytok.h"
#include "h_inc/gen.h"

/* ADDED BY TETISOFT */
#include <clib/string.h>
extern int longflag;

#if dLibs
#include <clib/ctype.h>
#endif

#if MMCC
overlay "pass2"
#endif

#if CC68
FILE *fopen();
#endif

#if NEEDBUF
char my_obuf[BUFSIZ];
#endif

#define T_SEG 0
#define D_SEG 1
#define B_SEG 2

#define TO_TEXT to_seg(T_SEG)
#define TO_DATA to_seg(D_SEG)
#define TO_BSS to_seg(B_SEG)

#define isareg(np) ((np)->g_token == REGVAR && (np)->g_rno >= AREG)

extern FILE *output;

static int in_seg;
static int lblnum;
static int dat_size;

/* This function heavily modified by TETISOFT */
/* Modified agian by VANSOFT. returns 0 on error else 1. */
int out_start(s)
char *s;
{
 if (s == NULL)
                return(NULL);

 if(!(output = fopen(s,"w"))) {
  fatals("Can`t open output file",s);
                return(NULL);
                }
#if NEEDBUF
 setbuf(output, my_obuf);
#endif
 in_seg = -1;
 lblnum = 0;
 dat_size = 0;

return(1); /* OK!!. */
}

out_end()
{
 if (output && output != stdout)
  fclose(output);
}

static char *sg_go[] = {
 ".text",
 ".data",
 ".bss"
};

to_text()
{
 TO_TEXT;
}

to_seg(sg)
{
 if (sg == in_seg)
  return;
 fprintf(output, "\t%s\n", sg_go[sg]);
 in_seg = sg;
}

o_aln(x)
{
 if (x && (dat_size & 1)) {
  dat_size++;
  TO_DATA;
  fprintf(output, "\t.even\n");
 }
}

char *rnms[] = {
 "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
 "a0", "a1", "a2", "a3", "a4", "a5", "a6", "sp",
};

char *regnm(n)
{
 return rnms[n];
}

#define regnm(x) rnms[x]

char *
init_str(n)
{
 char *s;

 switch (n) {
 case 1:
  s = ".dc.b"; break;
 case 2:
  s = ".dc.w"; break;
 default:
  s = ".dc.l"; break;
 }
 return s;
}

tlen(n)
{
 switch (n) {
 case 1:
  return 'b';
 case 2:
  return 'w';
 default:
  return 'l';
 }
}

out_fi(val)     /* V2 */
{      /* V2 */
 fprintf(output, "\t.dc.w\t$%x\n", val); /* V2 */
}      /* V2 */

o_vinit(tp, xp)
NODEP tp, xp;
{
 fprintf(output, "\t%s\t", init_str((int)tp->t_size));
 dat_size += tp->t_size;

 p2_expr(&xp);
 asn_chk(tp, xp);
 to_init(xp, tp);

 fputc('\n', output);
}

to_init(np, typ)
NODEP np, typ;
{
 NODEP tp;

 tp = allocnode();
 tp->e_token = TCONV;
 tp->n_tptr = typ;
 tp->n_flags |= N_COPYT;
 tp->n_left = np;
 tp->e_type = E_UNARY;
 strcpy(tp->n_name, "i cast");

 genx(tp, FORINIT);
}

out_advice(np)
NODEP np;
{
 long size;

 size = np->n_tptr->t_size;
 if (size == 0)
  return;

 switch (np->e_sc) {
 case K_AUTO:
  fprintf(output, ";var\t%d\t%d\t", (int)size,
   (int)np->e_offs);
  break;
 case K_REGISTER:
  fprintf(output, ";reg\t%d\t%s\t", (int)size,
   regnm(np->e_rno));
  break;
 default:
  return;
 }
 out_nm(np);
 fputc('\n', output);
}

out_argreg(np)
NODEP np;
{
 fprintf(output, "\tmove.%c\t%d(%s),%s\n",
  tlen((int)np->n_tptr->t_size), (int)np->e_offs,
  regnm(FRAMEP), regnm(np->e_rno));
}

out_fstart(np)
NODEP np;
{
 extern int pflag;

 TO_TEXT;

/* ADDED BY TETISOFT */
#ifdef FOR_AMIGA
 if (np->e_sc == K_STATIC) {
  fprintf(output, "\t.STATIC\t");
  und_nnm(np);
  fputc('\n', output);
 }
#endif

 if (np->e_sc != K_STATIC) {
  fprintf(output, "\t.globl\t");
  und_nnm(np);
  fputc('\n', output);
 }
 und_nnm(np);
 fprintf(output, ":\n"); 

 if (pflag) {
  int tlab = new_lbl();

  TO_BSS;
  fprintf(output, "L%d:\t.ds.l\t1\n", tlab);
  TO_TEXT;
  fprintf(output, "\tmove.l\t#");
  und_nnm(np);
  fprintf(output, ",a0\n");
  fprintf(output, "\tmove.l\t#L%d,a1\n", tlab);
  fprintf(output, "\tjsr\tmcount\n");
 }
}

static char rbuf[30];

char *
regstr(regs)
{
 int lod, hid, loa, hia;
 register i;
 char *bp = rbuf;

 lod = 999;
 hid = -1;
 for (i=DRV_START; i<=DRV_END; i++)
  if (regs & (1<<i)) {
   if (i < lod)  lod = i;
   if (i > hid)  hid = i;
  }
 loa = 999;
 hia = -1;
 for (i=ARV_START; i<=ARV_END; i++)
  if (regs & (1<<i)) {
   if (i < loa)  loa = i;
   if (i > hia)  hia = i;
  }
 if (lod < 999) {
  if (lod != hid)
   sprintf(bp, "%s-%s", rnms[lod], rnms[hid]);
  else
   sprintf(bp, "%s", rnms[lod]);
  if (loa < 999) {
   bp += strlen(rbuf);
   *bp++ = '/';
  }
 }
 if (loa < 999) {
  if (loa != hia)
   sprintf(bp, "%s-%s", rnms[loa], rnms[hia]);
  else
   sprintf(bp, "%s", rnms[loa]);
 }
 return rbuf;
}

out_fend(regs, lsize)
long lsize;
{
 if (lsize < 0x7fff)
  fprintf(output, "\tlink\t%s,#-%d\n", rnms[FRAMEP], (int)lsize);
 else
  fprintf(output, "\tlink\t%s,#0\n\tsub.l\t#%ld,sp\n",
   rnms[FRAMEP], lsize);
 if (regs)
  fprintf(output, "\tmovem.l\t%s,-(sp)\n", regstr(regs));
}

out_fret(regs, strl)
{
 if (regs)
  fprintf(output, "\tmovem.l\t(sp)+,%s\n", regstr(regs));
 if (strl)
  fprintf(output, "\tmove.l\t#L%d,a0\n", strl);
 fprintf(output, "\tunlk\t%s\n\trts\n", rnms[FRAMEP]);
}

out_fs(strl, size)
long size;
{
 TO_BSS;
 def_lbl(strl);
 if (size & 1)
  fprintf(output, "\t.ds.b\t%ld\n", size);
 else
  fprintf(output, "\t.ds.w\t%ld\n", size/2);
}

out_gv(np, isbss)
register NODEP np;
{
 long sz;
 char c;

 if (np->e_sc == K_STATIC) {
  np->e_offs = lblnum++;
 }

#ifdef FOR_AMIGA
/* JMM */
 if (np->e_sc == K_EXTERN) {
  fprintf(output, "\t.PUBLIC\t");
  out_nm(np);
  fputc('\n', output);
 }
#endif

 if (np->e_sc != K_EXTERN) {
  to_seg(isbss ? B_SEG : D_SEG);
  if (np->e_sc != K_STATIC) {
   fprintf(output, "\t.globl\t");
   out_nm(np);
   fputc('\n', output);
  }
  if (isbss) {
   if (np->e_sc == K_STATIC) {
    out_nm(np);
    sz = np->n_tptr->t_size;
    c = 'b';
    if (np->n_tptr->t_aln) {
     c = 'w';
     sz /= 2;
    }
    fprintf(output, ":\t.ds.%c\t%ld\n", c, sz);
   } else {
    fprintf(output, "\t.comm\t");
    out_nm(np);
    sz = np->n_tptr->t_size;
    if (sz & 1) sz++;  /* ALCYON hack */
    fprintf(output, ",%ld\n", sz);
   }
  } else {

/* ADDED BY TETISOFT */
/*
 * Since I can't decide whether the variable to initialize here
 * is accessed as byte or not, let's force it to be word-aligned
 * to avoid alignment errors of A68k or maybe greetings from India
 */
#ifdef FOR_AMIGA
   o_aln(1);
#endif

   out_nm(np);
   fprintf(output, ":\n");
  }
 }
}

new_lbl()
{
 return lblnum++;
}

def_lbl(l)
{
 fprintf(output, "L%d:\n", l);
}

out_br(l)
{
 if (l < 0)
  error("bad branch");
 else
  fprintf(output, "\tbra\tL%d\n", l);
}

static char *bnm[] = {
 "",
 "beq",
 "bne",
 "blt",
 "bge",
 "ble",
 "bgt",
 "bra",
 "nop",
 "bcs",
 "bcc",
 "bls",
 "bhi"
};

static char *snm[] = {
 "",
 "seq",
 "sne",
 "slt",
 "sge",
 "sle",
 "sgt",
 "",
 "",
 "scs",
 "scc",
 "sls",
 "shi"
};

out_b(key, l)
{
 if (key != B_NO)
  fprintf(output, "\t%s\tL%d\n", bnm[key], l);
}

out_bnol(key)
{
 fprintf(output, "\t%s\t", bnm[key]);
}

out_snol(key)
{
 fprintf(output, "\t%s\t", snm[key]);
}

out_d0cmp(x)
{

/* CHANGED BY TETISOFT */
/* 'case' labels are always 'int' */
/* fprintf(output, "\tcmp.w\t#%d,d0\n", x); */
 fprintf(output, longflag ? "\tcmp.l\t#%d,d0\n" :
       "\tcmp.w\t#%d,d0\n", x);

}

out_d0sub(x)
{

/* CHANGED BY TETISOFT */
/* 'case' labels are always 'int' */
/* fprintf(output, "\tsub.w\t#%d,d0\n", x); */
 fprintf(output, longflag ? "\tsub.l\t#%d,d0\n" :
       "\tsub.w\t#%d,d0\n", x);

}

out_tlbl(l)
{
 fprintf(output, "\t.dc.l\tL%d\n", l);
}

out_tsw()
{
 fprintf(output, "\text.l\td0\n");
 fprintf(output, "\tasl.l\t#2,d0\n");
 fprintf(output, "\tmove.l\t4(pc,d0.l),a0\n");
 fprintf(output, "\tjmp\t(a0)\n");
}

out_nm(np)
NODEP np;
{
 if (np->e_sc == K_STATIC)
  fprintf(output, "L%d", (int)np->e_offs);
 else
  und_nnm(np);
}

out_zi(tp)
NODEP tp;
{
 char *s;
/*
 switch (tp->t_token) {
 case K_FLOAT:
  fprintf(output, "\t.float\t0.0\n"); return;
 case K_DOUBLE:
  fprintf(output, "\t.double\t0.0\n"); return;
 }
*/
 dat_size += tp->t_size;
 s = init_str((int)tp->t_size);
 fprintf(output, "\t%s\t0\n", s);
}

o_nz(sz, aln)
long sz;
{
 dat_size += sz;
 if (aln) {
  if (sz & 1)
   fprintf(output, "\t.ds.b\t1\n");
  sz >>= 1;
  fprintf(output, "\t.ds.w\t%ld\n", sz);
 } else {
  fprintf(output, "\t.ds.b\t%ld\n", sz);
 }
}

dumpstrs(np)
NODEP np;
{

/* ADDED BY TETISOFT, avoid empty hunks */
 if (np == NULL)
  return;

 TO_DATA;
more:
 if (np == NULL)
  return;
 fprintf(output, "L%d:", (int)np->g_offs);
 out_scon(np);
 np = np->n_next;
 goto more;
}

int see_esc;

out_scon(np)
NODEP np;
{
 int len = 0;

 if (np == NULL)
  return 0;
 see_esc = 0;
more:
 if (np->n_name[0]) {
  fprintf(output, "\t.dc.b\t");
  len += out_str(np->n_name);
  putc('\n', output);
 }
 np = np->n_nmx;
 if (np)
  goto more;
 fprintf(output, "\t.dc.b\t0\n");
 len++;
 dat_size += len;
 return len;
}

out_str(s)
char *s;
{
 int len;
 register c;

 len = 0;
 for ( ; c = *s; s++) {
  if (see_esc) { /* allow null */
   c--;
   see_esc = 0;
  } else if (c == 1) {
   see_esc = 1;
   continue;
  }
  if (len) {
   if ((len & 15) == 0)
    fprintf(output, "\n\t.dc.b\t");
   else
    putc(',', output);
  }
  out_1c(c);
  len++;
 }
 return len;
}

out_asm(np)
NODEP np;
{
 putc('\t', output);
more:
 fprintf(output, "%s", np->n_name); /* no \0 or \1 please! */
 np = np->n_nmx;
 if (np)
  goto more;
 putc('\n', output);
}

und_nnm(np)
NODEP np;
{
 fputc('_', output);
 fput_nnm(np);
}

out_1c(c)
char c;
{
 fprintf(output, "$%x", c & 0xff);
}

outcode(np)
register NODEP np;
{
 NODEP tp;

 if (np == NULL) return;

 switch (np->g_type) {
 case EV_NONE:
  break;
 case EV_RL:
  outcode(np->n_right);
  outsub(np->g_betw, np);
  /* fall through */
 case EV_LEFT:
  outcode(np->n_left);
  break;
 case EV_LR:
 case EV_LRSEP:
  outcode(np->n_left);
  outsub(np->g_betw, np);
  /* fall through */
 case EV_RIGHT:
  outcode(np->n_right);
  break;
 default:
                error("bad evaluation type"); /* VANSOFT.*/
  /* printf("bad eval %d ", np->g_type); */
 }
 if (np->n_flags & N_COPYT) /* g_code is a char * */
  outsub(np->g_code, np);
 else    /* g_code is a list of nodes */
  for (tp=np->g_code; tp != NULL; tp = tp->g_code)
   outsub(tp->n_name, np);
}

outsub(cp, np)
register char *cp;
register NODEP np;
{
 register char c;

 if (cp == NULL) return;
 while (c = *cp++)
  if (c == '<')
   out_let(*cp++, np->n_left);
  else if (c == '>')
   out_let(*cp++, np->n_right);
  else if (c == '\'') {
   c = *cp++;
   fputc(c, output);
  } else if (c == 'L')
   seelab(*cp++, np);
  else if (c == 'R')
   seereg(np, *cp++);
  else if (c >= 'A' && c <= 'Z') {
   out_let(c, np);
  } else
   fputc(c, output);
}

seereg(np, c)
NODEP np;
{
 int i;

 switch (c) {
 case '0': i = np->g_rno;  break;
 case '1': i = np->g_r1;   break;
 case '2': i = np->g_r2;   break;
 }
 fprintf(output, regnm(i)); 
}

out_let(c, np)
register NODEP np;
{
 int i;

 switch (c) {
 case 'A':
  if (np->g_flags & IMMEDID)
   fputc('#', output);
  out_a(np, output);
  break;
 case 'E':
 case 'F': /* branch if false */
  i = cctok(np);
  i = (i&1) ? i+1 : i-1; /* reverse truth */
  if (c == 'F')
   out_bnol(i);
  else
   out_snol(i);
  break;
 case 'H': /* last a reg (for struct assign) */

/* CHANGED BY TETISOFT */
/*  fprintf(output, regnm(ARV_START-1)); */
  fprintf(output, regnm(AR_TEMP));

  break;
 case 'K':
  fprintf(output, "%ld", np->g_bsize);
  break;
 case 'N':
  fprintf(output, "%s", np->n_name);
  break;
 case 'O':
  fprintf(output, "%ld", np->g_offs);
  break;
 case 'Q':
  if (np->g_flags & IMMEDID) {
   warn("constant test expr");
   if (np->g_token == ICON && np->g_offs == 0)
    fprintf(output, "\tor\t#$FF,ccr\n");
   else
    fprintf(output, "\tand\t#0,ccr\n");
   return;
  }
  fprintf(output, "\t%s.%c\t", isareg(np) ? "cmp" : "tst",
   tlen(np->g_sz));
  if (isareg(np))
   fprintf(output, "#0,");
  out_let('A', np);
  fputc('\n', output);
  break;
 case 'S':
  fputc(tlen(np->g_sz), output);
  break;
 case 'T': /* branch if true */
  out_bnol(cctok(np));
  break;
 case 'U':
  fputc(np->g_ty == ET_U ? 'u' : 's', output);
  break;
 case 'W': /* field width 1's */
  fprintf(output, "$%x", ones(np->g_fldw));
  break;
 case 'X': /* ~(W << offset) */

/* CHANGED BY TETISOFT */
/*  fprintf(output, "$%x", (unsigned short) */
  fprintf(output, "$%x",

    (~(ones(np->g_fldw)<<np->g_fldo)));
  break;
 case 'Y': /* field offset */
  fprintf(output, "%d", np->g_fldo);
  break;

/* CHANGED BY TETISOFT */
/* case 'Z': */ /* field offset - 8 */
/*  fprintf(output, "%d", np->g_fldo - 8); */
 case 'Z': /* field offset & 7 */
  i = np->g_fldo & 7;
  if (i != 0)
   fprintf(output, "%d", np->g_fldo & 7);
  else
   fprintf(output, "%d", 8);

  break;
 default:
                error("bad out_let");     /* Changed by VANSOFT. */
  /* printf("bad out_let %c ", c); */
 }
}

#ifdef UNIX      /* V2 */
/*       /* V2 */
 * conversion routine for IEEE based cross compile /* V2 */
 */       /* V2 */
long       /* V2 */
ieee_to_ffp(d)      /* V2 */
double d;      /* V2 */
{       /* V2 */
 union {      /* V2 */
  float fval;    /* V2 */
  long ival;    /* V2 */
 } v;      /* V2 */
 int sign, exp;     /* V2 */
 long mant, rv;     /* V2 */
       /* V2 */
 v.fval = d;     /* V2 */
       /* V2 */
 sign = (v.ival < 0) ? 0x80 : 0;   /* V2 */
 exp = (v.ival >> 23) & 0xff;   /* V2 */
 exp -= (127 - 1);    /* V2 */
 mant = 0x800000L | (v.ival & 0x7fffffL); /* V2 */
       /* V2 */
 if (exp <= -64) {    /* V2 */
  return 0;    /* V2 */
 } else if (exp > 63) {    /* V2 */
  exp = 63;    /* V2 */
  mant = 0xffffff;   /* V2 */
 }      /* V2 */
       /* V2 */
 rv = (mant << 8) | sign | (exp + 64);  /* V2 */
 return rv;     /* V2 */
}       /* V2 */
#endif       /* V2 */

out_a(np, fd)
register NODEP np;
FILE *fd;
{
 int offs = np->g_offs;

 switch (np->g_token) {
 case ICON:
  fprintf(fd, "%ld", np->g_offs);
  break;
 case FCON:
#ifndef UNIX       /* V2 */
  /* works for ALCYON C */
  /* otherwise depends on floating internal format */
  fprintf(fd, "$%lx", np->g_offs);
#else        /* V2 */
  fprintf(fd, "$%lx", ieee_to_ffp(np->g_fval)); /* V2 */
#endif        /* V2 */
  break;
 case ONAME:
  while (np->g_flags & (CHILDNM|RCHILDNM)) {
   np = (np->g_flags & CHILDNM) ? 
    np->n_left : np->n_right;
  }
  qput_nnm(np, fd);
  if (offs)
   fprintf(fd, offs > 0 ? "+%d" : "%d", offs);
  break;
 case PUSHER:
  fprintf(fd, "(sp)+");
  break;
 case OREG:
  if (offs)
   fprintf(fd, "%d", offs);
  fprintf(fd, "(%s)", regnm(np->g_rno));
  break;
 case REGVAR:
  fprintf(fd, regnm(np->g_rno));
  break;
 case ',':
  fputc(',', fd);  /* for debug */
  break;
 default:
  if (np->g_token >= BR_TOK) {
   fprintf(fd, "B_%s", bnm[np->g_token - BR_TOK]);
   break;
  }
                error("Unkown token?");  /* VANSOFT. */
  /* printf("? tok %d ", np->g_token); */
 }
}

seelab(c, np)
char c;
NODEP np;
{
 c -= '1';
 fprintf(output, "L%d", (int)np->g_bsize+c);
}

ones(n)
{
 return (1 << n) - 1;
}
