/* Copyright (c) 1988 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
 *
 *   Revised: Dec 1988	Joe Montgomery
 *
 *   Revised out.c to use MOTOROLA assembly directives in order
 *	to be compatible with C.Gibbs a68k assembler & blink
 *	Added END statement
 *	Changed .comm label,size to label DC.x 0
 *
 *     other modules:
 *   Revised main.c to use Amiga File System Naming Conventions
 *	Added ?,C,F switches. ? help
 *			      C force data,bss into Chip memory
 *			      F force data,bss into Fast memory
 *   Revised d2.c so that externs are declared as XREF -----
 *
 *   Revised g2.c & gen.c to declare all called functions XREF
 *     (will need to change this to declare only external functions)
 *
 *   All changes labeled JMM
 *
 *	Code generation output routines.
 */

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


#ifdef dLibs
#include <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;


/*JMM added -O switch to allow user to specify output file */
char *outfilename,*errorfile;
extern int nmerrors;

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

/* called to open output file. We've only just begun */
out_start(s)
char *s;
{
	char *scopy(), *outs;
	register int len;

/* JMM added -O switch to allow user to specify output file */
	if(outfilename) outs = scopy(outfilename);
	     else outs = scopy(s);
	len = strlen(outs);
/* JMM add -O switch */
	if(outfilename){
		output = fopen(outs,"w");
		if(output == NULL )
			fatals("Cant open",outs);
	} else if ( len >= 2 && outs[len-2] == '.' &&
			tolower(outs[len-1]) == 'c' ) {
		outs[len-1] = 's';
		output = fopen(outs, "w");
		if (output == NULL)
			fatals("Cant open", outs);
#if NEEDBUF
		setbuf(output, my_obuf);
#endif
	} else
		output = stdout;
	sfree(outs);

	in_seg = -1;
	lblnum = 0;
	dat_size = 0;
}

/* closes output file.	All done */
static char seterrorbuf[50];
out_end()
{
	char *outs;
	extern char *scopy();
	FILE *err;

	fprintf(output, "\tEND \n");

	if (output != stdout)
		fclose(output);
	if( ( errorfile != NULL) ){
		outs = scopy((char *) errorfile );
		err = fopen(outs,"w");
		if(err == NULL )
			fatals("Cant open",outs);
		fprintf(err,"%d",nmerrors);
		close(err);
		sfree(outs);
	}
}

/* Assembler segment Directives */
static char *sg_go[] = {
	"CODE CODE",   /* .text  segment  ?same as code segment? */
	"DATA DATA",   /* .data  segment */
	"BSS BSS"     /* .bss  segment */
};
/* JMM added ability to force Data,BSS into either Chip or Fast */
short usefastmemory,usechipmemory;

static char *chipsg_go[] = {
	"CODE CODE",   /* .text  segment  ?same as code segment? */
	"DATA CHIPDATA CHIP",   /* .data  segment */
	"BSS CHIPBSS CHIP"     /* .bss  segment */
};

static char *fastsg_go[] = {
	"CODE CODE",   /* .text  segment  ?same as code segment? */
	"DATA FASTDATA FAST",   /* .data  segment */
	"BSS FASTBSS FAST"     /* .bss  segment */
};

/* extern directive, global directive */
char externdir[]="\tXREF \t";
char globaldir[]="\tXDEF \t";
char externfuncdir[]="\tXREF \t";

to_text()
{
	TO_TEXT;   /* to_seg(0) *//* fprintf(output,"   .text ") */
}

to_seg(sg)
{
char *segment;
/* JMM modified to_seg to force data,bss into either CHIP or FAST */
	if (sg == in_seg)
		return;
	if(usechipmemory)segment =(char *) chipsg_go[sg];
	   else if(usefastmemory) segment = (char *)fastsg_go[sg];
	   else segment =(char *) sg_go[sg];
	fprintf(output, "\t%s\n", segment);
	in_seg = sg;
}

/* JMM ? output a long ? */
o_aln(x)
{
	if (x && (dat_size & 1)) {
		dat_size++;
		TO_DATA;	  /* data segment */
		fprintf(output, "\tCNOP 0,2\n");/* was .even */
	}
}

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

/* return string containing register name */
char *regnm(n)
{
	return rnms[n];
}

/*     define constant of length n */
char *
init_str(n)
int 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';
	}
}

/* JMM ? output  init node pointer	     *
 *   output predefined values for variable ? */
o_vinit(tp, xp)
NODEP tp, xp;	     /* Node pointers */
{
 /*  .dc.b   t_size 1
  *  .dc.w	    2
  *  .dc.l	    3 or greater
  */
	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);
}

/* output move.x  #(a6),rn    where x=b,w,l , rn=a0-a7 or d0-d7*/
out_argreg(np)
NODEP np;
{
	fprintf(output, "\tmove.%c\t%d(a6),%s\n",
		tlen((int)np->n_tptr->t_size), (int)np->e_offs,
		regnm(np->e_rno));
}


extern	int	pflag;

/* JMM ? output label ? makes function labels etc */
out_fstart(np)
NODEP np;
{
	TO_TEXT; /* code segment */
	if (np->e_sc != K_STATIC) {
/* JMM changed to output XDEF name */
		fprintf(output, globaldir);
		und_nnm(np); /* output _XXXXX  where XXXXX is name of variable */
		fputc('\n', output);
	}
	und_nnm(np);
	fprintf(output, ":\n");

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

		TO_BSS;
		fprintf(output, "L%d:\tDS.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,"\tXREF \tmcount\n");
		fprintf(output,"; Calling profiler \n");
		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, "d%d-d%d", lod, hid);
		else
			sprintf(bp, "d%d", lod);
		if (loa < 999) {
			bp += strlen(rbuf);
			*bp++ = '/';
		}
	}
	if (loa < 999) {
		if (loa != hia)
			sprintf(bp, "a%d-a%d", loa-AREG, hia-AREG);
		else
			sprintf(bp, "a%d", loa-AREG);
	}
	return rbuf;
}

out_fend(regs, lsize)
long lsize;
{
	if (lsize < 0x7fff)
		fprintf(output, "\tlink\ta6,#-%d\n", (int)lsize);
	else
		fprintf(output, "\tlink\ta6,#0\n\tsub.l\t#%ld,sp\n",
			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\ta6\n\trts\n");
}

out_fs(strl, size)
long size;
{
	TO_BSS;
	def_lbl(strl);
	fprintf(output, "\tDS.W \t%ld\n", size/2);
}

/* ? output global variables ? */
out_gv(np, isbss)
register NODEP np;
{
	long sz;
	char c;

	if (np->e_sc == K_STATIC) {
		np->e_offs = lblnum++;
	}
	 /* JMM ? added part to print out XREF statement for a68k ? */
	if (np->e_sc == K_EXTERN) {
		to_seg(isbss ? B_SEG : D_SEG);
		/*if (np->e_sc != K_STATIC) { */
			fprintf(output, externdir);
			out_nm(np);
			fputc('\n', output);
	      /*  }				*/
	}
	if (np->e_sc != K_EXTERN) {
		to_seg(isbss ? B_SEG : D_SEG);
		if (np->e_sc != K_STATIC) {
			fprintf(output, globaldir);
			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, ":\tDS.%c\t%ld\n", c, sz);
			} else {
				out_nm(np);
				sz = np->n_tptr->t_size;
				if (sz & 1) sz++;  /* ALCYON hack */
				fprintf(output, ":\t DS.%c 0 \n", tlen(sz) );
				sz -= 4;
				while (sz > 0){
				  fprintf(output,"\t DS.W 0\n");
				  sz -= 2;
				}


/*				  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 {
			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"
};

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_d0cmp(x)
{
	fprintf(output, "\tcmp.w\t#%d,d0\n", x);
}

out_d0sub(x)
{
	fprintf(output, "\tsub.w\t#%d,d0\n", x);
}

out_tlbl(l)
{
	fprintf(output, "\tDC.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);
}

externfuncref(np)
NODEP np;
{
	if (  np->e_sc != K_STATIC){
		fprintf(output, externfuncdir);
		fput_nnm(np->n_left);
		fprintf(output,"\n");
	}
}


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, "\tDS.B\t1\n");
		sz >>= 1;
		fprintf(output, "\tDS.W\t%ld\n", sz);
	} else {
		fprintf(output, "\tDS.B\t%ld\n", sz);
	}
}

dumpstrs(np)
NODEP np;
{
	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, "\tDC.B\t");
		len += out_str(np->n_name);
		putc('\n', output);
	}
	np = np->n_nmx;
	if (np)
		goto more;

	fprintf(output, "\tDC.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)
			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);
}


/* Output  underscore name */
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:
		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 'F':       /* branch if false */
		i = cctok(np);
		i = (i&1) ? i+1 : i-1;  /* reverse truth */
		out_bnol(i);
		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) */
		fprintf(output, "$%x", ~(ones(np->g_fldw)<<np->g_fldo));
		break;
	case 'Y':       /* field offset */
		fprintf(output, "%d", np->g_fldo);
		break;
	case 'Z':       /* field offset - 8 */
		fprintf(output, "%d", np->g_fldo - 8);
		break;
	default:
		printf("bad out_let %c ", c);
	}
}

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:
		/* works for ALCYON C */
		/* otherwise depends on floating internal format */
		fprintf(fd, "$%lx", np->g_offs);
		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;
		}
		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;
}

