/*
 * C compiler
 * ==========
 *
 * Copyright 1989, 1990, 1991 Christoph van Wuellen.
 * Credits to Matthew Brandt.
 * All commercial rights reserved.
 *
 * This compiler may be redistributed as long there is no
 * commercial interest. The compiler must not be redistributed
 * without its full sources. This notice must stay intact.
 *
 * History:
 *
 * 1989   starting an 68000 C compiler, starting with material
 *        originally by M. Brandt
 * 1990   68000 C compiler further bug fixes
 *        started i386 port (December)
 * 1991   i386 port finished (January)
 *        further corrections in the front end and in the 68000
 *        code generator.
 *        The next port will be a SPARC port
 */

#include "config.h"

#define	MAX_WIDTH	60

#ifdef MC680X0
#ifdef TARGET_ACK

#include "chdr.h"
#include "expr.h"
#include "cglbdec.h"
#include "proto.h"
#include "gen68k.h"
#include "outproto.h"
#include "version.h"
#include <time.h>

#ifdef MULTIPLE_ASSEMBLERS
#define PRIVATE	static
#undef	put_code
#undef	put_name
#undef	put_label
#undef	put_byte
#undef	put_char
#undef	put_word
#undef	put_long
#undef	put_pointer
#undef	put_short
#undef	put_storage
#undef	put_literals
#undef	put_reference
#undef	put_cseg
#undef	put_dseg
#undef	put_kseg
#undef	put_float
#undef	put_double
#undef	put_longdouble
#undef	put_start
#undef	put_finish
#else
#define	PRIVATE
#endif /* MULTIPLE_ASSEMBLERS */

enum e_gt {
    bytegen, wordgen, longgen, stringgen, floatgen, nogen
};
enum e_sg {
    noseg, codeseg, dataseg, bssseg, romseg
};

#if defined(__STDC__) || defined(__cplusplus)
#define P_(s) s
#else
#define P_(s) ()
#endif

static void	nl		P_((void));
static void	putop		P_((OPCODE));
static void	putconst	P_((const EXPR *));
static void	putlen		P_((ILEN));
static void	putamode	P_((const ADDRESS *, ILEN));
static void	put_mask	P_((REGMASK));
static void	put_rmask	P_((REGMASK));
static void	put_smask	P_((REGMASK));
static void	putreg		P_((REG));
static void	put_header	P_((enum e_gt, SIZE));
static void	seg		P_((enum e_sg, const char *, SIZE));
static void	put_bseg	P_((SIZE));
static void	put_align	P_((SIZE));
PRIVATE void	put_name	P_((SYM *));
PRIVATE void	put_long	P_((long));
PRIVATE void	put_cseg	P_((SIZE));
PRIVATE void	put_dseg	P_((SIZE));
PRIVATE void	put_kseg	P_((SIZE));
PRIVATE void	put_label	P_((LABEL));
PRIVATE void	put_reference	P_((SYM *));
PRIVATE void	put_byte	P_((int));
PRIVATE void	put_char	P_((int));

#undef P_

/* variable initialization */

static enum e_gt gentype = nogen;
static enum e_sg curseg = noseg;
static int       outcol = 0;
static SIZE	 align_type = 0L;
static char	*prefix = "I_";
static char	*comment = "!";

static char *opl[] = {
	"move",		/* op_move */
	"move.l",	/* op_moveq */
	"move",		/* op_movea */
	"add",		/* op_add */
	"add",		/* op_addi */
	"add",		/* op_addq */
	"add",		/* op_adda */
	"sub",		/* op_sub */
	"sub",		/* op_subi */
	"sub",		/* op_subq */
	"sub",		/* op_suba */
	"muls",		/* op_muls */
	"mulu",		/* op_mulu */
	"divs",		/* op_divs */
	"divu",		/* op_divu */
	"and",		/* op_and */
	"and",		/* op_andi */
	"or",		/* op_or */
	"or",		/* op_ori */
	"eor",		/* op_eor */
	"asl",		/* op_asl */
	"lsr",		/* op_lsr */
	"jmp",		/* op_jmp */
	"jsr",		/* op_jsr */
	"bsr",		/* op_brs */
	"movem",	/* op_movem */
	"rts",		/* op_rts */
	"bra",		/* op_bra */
	"beq", 		/* op_beq */
	"bne",		/* op_bne */
	"blt",		/* op_blt */
	"ble",		/* op_ble */
	"bgt",		/* op_bgt */
	"bge",		/* op_bge */
	"bhi", 		/* op_bhi */
	"bcc", 		/* op_bhs */
	"bcs", 		/* op_blo */
	"bls", 		/* op_bls */
	"btst",		/* op_btst */
	"tst",		/* op_tst */
	"ext",		/* op_ext */
	"extb",		/* op_extb */
	"lea",		/* op_lea */
	"swap",		/* op_swap */
	"neg",		/* op_neg */
	"not",		/* op_not */
	"cmp",		/* op_cmp */
	"cmp",		/* op_cmpa */
	"clr",		/* op_clr */
	"link",		/* op_link */
	"unlk",		/* op_unlk */
	"pea",		/* op_pea */
	"cmp",		/* op_cmpi */
	"dbra",		/* op_dbra */
	"asr",		/* op_asr */
	"rol",		/* op_rol */
	"ror",		/* op_ror */
	"seq", 		/* op_seq */
	"sne",		/* op_sne */
	"slt",		/* op_slt */
	"sle",		/* op_sle */
	"sgt",		/* op_sgt */
	"sge",		/* op_sge */
	"shi", 		/* op_shi */
	"scc", 		/* op_shs */
	"scs", 		/* op_slo */
	"sls", 		/* op_sls */
	"st",		/* op_st */
	"nop",		/* op_nop */
#ifdef FLOAT_IEEE
	"fabs",		/* op_fabs */
	"fneg",		/* op_fneg */
	"fadd",		/* op_fadd */
	"fsub",		/* op_fsub */
	"fdiv",		/* op_fdiv */
	"fmul",		/* op_fmul */
	"fcmp",		/* op_fcmp */
	"ftst",		/* op_ftst */
	"fmove",	/* op_fmove */
	"fmovem",	/* op_fmovem */
#endif /* FLOAT_IEEE */
#ifdef ASM
	"",		/* op_asm */
#endif /* ASM */
	".line",	/* op_line */
	0,		/* op_label */
};

static void
putop P1(OPCODE, op)
{
    if (op >= OP_MIN && op <= OP_MAX && opl[op] != (char *)0) {
	oprintf ("\t%s",opl[op]);
    } else {
	FATAL ((__FILE__,"putop","illegal opcode %d",op));
    }
}

/*
 * put a constant to the output file.
 */
static void
putconst P1(const EXPR*, ep)
{
    switch (ep->nodetype) {
      case en_autocon:
      case en_icon:
	oprintf("%ld", ep->v.i);
	break;
#ifndef FLOAT_BOOTSTRAP
#ifdef FLOAT_MFFP
      case en_fcon:
	oprintf("0x%lx", genffp(ep->v.f));
	break;
#endif /* FLOAT_MFFP */
#endif /* FLOAT_BOOTSTRAP */
      case en_labcon:
	oprintf("%s%u", prefix, (unsigned)ep->v.l);
	break;
      case en_nacon:
	oprintf("%s",outlate(ep->v.str));
	break;
      case en_sym:
	oprintf("%s",outlate(ep->v.sp->name));
	break;
      case en_add:
	putconst(ep->v.p[0]);
	oprintf("+");
	putconst(ep->v.p[1]);
	break;
      case en_sub:
	putconst(ep->v.p[0]);
	oprintf("-");
	putconst(ep->v.p[1]);
	break;
      case en_uminus:
	oprintf("-");
	/*FALLTHRU*/
      case en_cast:
	putconst(ep->v.p[0]);
	break;
      case en_str:
        oprintf("%s",ep->v.str);
	break;
      default:
	FATAL ((__FILE__,"putconst","illegal constant node %d",ep->nodetype));
	break;
    }
}

/*
 * append the length field to an instruction.
 */
static void
putlen P1(ILEN, l)
{
    switch (l) {
      case IL0:
	break;
      case IL1:
	oprintf(".b");
	break;
      case IL2:
	oprintf(".w");
	break;
      case IL4:
	oprintf(".l");
	break;
      case IL4+1:
	oprintf(".s");
	break;
      case IL8+1:
	oprintf(".d");
	break;
      case IL12+1:
	oprintf(".x");
	break;
      default:
	FATAL ((__FILE__,"putlen","illegal length field %d", (int)l));
	break;
    }
}

/*
 * output a general addressing mode.
 */
static void
putamode P2(const ADDRESS*, ap, ILEN, len)
{
    IVAL i_val;

    switch (ap->mode) {
      case am_immed:
	oprintf("#");
        /*
         * Suppress overflow in immediate arguments - 
         * which may occur due to optimization of constants
         */
        if (ap->u.offset->nodetype == en_icon) {
            i_val = ap->u.offset->v.i;
	    switch (len) {
	      case IL1:
		i_val &= (IVAL)0x000000ffL;
		break;
	      case IL2:
		 i_val &= (IVAL)0x0000ffffL;
		break;
	      default:
		break;
	    }
            oprintf("%ld",i_val);
            break;
        }
	/*FALLTHRU*/
      case am_direct:
	putconst(ap->u.offset);
	break;
      case am_areg:
	oprintf("a%d", (int)ap->preg-(int)A0);
	break;
      case am_dreg:
	oprintf("d%d", (int)ap->preg);
	break;
      case am_ind:
	oprintf("(a%d)", (int)ap->preg-(int)A0);
	break;
      case am_ainc:
	oprintf("(a%d)+", (int)ap->preg-(int)A0);
	break;
      case am_adec:
	oprintf("-(a%d)", (int)ap->preg-(int)A0);
	break;
      case am_indx:
        /* allow 32-bit offsets */
	putconst(ap->u.offset);
	oprintf("(a%d)", (int)ap->preg-(int)A0);
	break;
      case am_indx2:
        /* allow 32-bit offsets */
	putconst(ap->u.offset);
	oprintf("(a%d,d%d.%c)", (int)ap->preg-(int)A0, (int)ap->sreg, 'l');
	break;
      case am_indx3:
        /* allow 32-bit offsets */
	putconst(ap->u.offset);
	oprintf("(a%d,a%d.l)", (int)ap->preg-(int)A0, (int)ap->sreg-(int)A0);
	break;
      case am_indx4:
        /* allow 32-bit offsets */
	putconst(ap->u.offset);
	oprintf("(a%d,d%d.%c)", (int)ap->preg-(int)A0, (int)ap->sreg, 'w');
	break;
      case am_rmask:
	put_rmask(ap->u.mask);
	break;
      case am_smask:
	put_smask(ap->u.mask);
	break;
      case am_freg:
	oprintf("fp%d", (int)ap->preg-(int)FP0);
	break;
      case am_line:
      case am_str:
	putconst(ap->u.offset);
	break;
      default:
	FATAL ((__FILE__,"putamode","illegal address mode %d",ap->mode));
	break;
    }
}

/*
 * output a generic instruction.
 */
PRIVATE void
put_code P1(const CODE*, ip)
{
    putop(ip->opcode);
    putlen(ip->length);
    if (ip->oper1 != NIL_ADDRESS) {
	oprintf("\t");
	putamode(ip->oper1,ip->length);
	if (ip->oper2 != NIL_ADDRESS) {
	    if (ip->opcode == op_line) {
		oprintf("\n%s>>>>\t",comment);
	    } else {
		oprintf(",");
	    }
	    putamode(ip->oper2,ip->length);
	}
    }
    oprintf("%s", newline);
}

/*
 * generate a register mask.
 */
static void
put_mask P1(REGMASK, mask)
{
    REG		 reg;
    BOOL	 pending = FALSE;
    for (reg = D0; reg <= FP7; reg++) {
	if (mask & (REGMASK)1) {
	    if (pending)
		oprintf("/");
	    putreg(reg);
	    pending = TRUE;
	}
	mask >>= 1;
    }
}

/*
 * generate a register mask for save.
 */
static void
put_smask P1(REGMASK, mask)
{
    put_mask (mask);
}

/*
 * generate a register mask for restore.
 */
static void
put_rmask P1(REGMASK, mask)
{
    put_mask (mask);
}

/*
 * generate a register name from a tempref number.
 */
static void
putreg P1(REG, r)
{
    switch (r) {
      case D0:
      case D1:
      case D2:
      case D3:
      case D4:
      case D5:
      case D6:
      case D7:
	oprintf("d%d", (int)r);
	break;
      case A0:
      case A1:
      case A2:
      case A3:
      case A4:
      case A5:
      case A6:
      case A7:
	oprintf("a%d", (int)r - (int)A0);
	break;
      case FP0:
      case FP1:
      case FP2:
      case FP3:
      case FP4:
      case FP5:
      case FP6:
      case FP7:
	oprintf("fp%d", (int)r - (int)FP0);
	break;
      default:
	CANNOT_REACH_HERE();
    }
}

PRIVATE void
put_reference P1(SYM*, sp)
{
    if (!is_symbol_output(sp)) {
	switch (sp->storage_class) {
	  case sc_global:
	    nl();
	    oprintf("\t.extern %s%s",outlate(sp->name), newline);
	    break;
	  case sc_external:
	    break;
	  default:
	    break;
	}
	symbol_output(sp);
    }
}

/*
 * generate a named label.
 */
PRIVATE void
put_name P1(SYM*, sp)
{
    put_reference(sp);
    oprintf("%s:%s",outlate(sp->name), newline);
}

/*
 * output a compiler generated label.
 */
PRIVATE void
put_label P1(LABEL, lab)
{
    oprintf("%s%u:%s", prefix, (unsigned int) lab, newline);
}

static void
put_header  P2(enum e_gt, gtype, SIZE, al)
{
    static char *directive[] = {
	".data1\t",	/* bytegen */
	".data2\t",	/* wordgen */
	".data4\t",	/* longgen */
	".ascii\t\"",	/* stringgen */
	};

    if (gentype != gtype || outcol >= MAX_WIDTH) {
	put_align(al);
	gentype = gtype;
	outcol = 15;
	oprintf("\t%s", directive[gtype]);
    } else if (gentype != stringgen) {
	oprintf(",");
    }
}

PRIVATE void
put_byte P1(int, val)
{
    put_header(bytegen, alignment(tp_char));
    oprintf("%d", val & 0x00ff);
    outcol += 4;
}

PRIVATE void
put_char P1(int, val)
{
    if (val >= 32 && val <= 126 && val != '\\' && val != '"') {
	put_header(stringgen, alignment(tp_char));
	oprintf("%c", val);
	outcol++;
    } else
	put_byte(val);
}

PRIVATE void
put_word P1(int, val)
{
    put_header(wordgen, alignment(tp_short));
    oprintf("%d", (int)((unsigned)val & (unsigned)0xffffL));
    outcol += 6;
}

#ifndef FLOAT_BOOTSTRAP
#ifdef FLOAT_IEEE
/*
 * Generate IEEE single and double numbers
 */
PRIVATE void
put_float P1(RVAL *, vp)
{
    unsigned long ul;
    ieee_single(vp, &ul);
    put_long((long) ul);
}

PRIVATE void
put_double P1(RVAL *, vp)
{
    unsigned long ul[2];
    ieee_double(vp, ul, TRUE);
    put_long ((long) ul[0]);
    put_long ((long) ul[1]);
}

PRIVATE void
put_longdouble P1(RVAL *, vp)
{
    unsigned long ul[3];
    ieee_longdouble(vp, ul, TRUE);
    put_long ((long) ul[0]);
    put_long ((long) ul[1]);
    put_long ((long) ul[2]);
}
#endif /* FLOAT_IEEE */
#ifdef FLOAT_MFFP
/*
 * Generate MOTOROLA FFP numbers
 */
PRIVATE void
put_float P1(const RVAL *, vp)
{
    put_long((long) genffp(val));
}

PRIVATE void
put_double P1(const RVAL *, vp)
{
    put_long((long) genffp(vp));
}

PRIVATE void
put_longdouble P1(RVAL, val)
{
    put_long((long) genffp(val));
}
#endif /* FLOAT_MFFP */
#endif /* FLOAT_BOOTSTRAP */

PRIVATE void
put_long P1(IVAL, val)
{
    put_header(longgen, alignment(tp_long));
    oprintf("0x%lx", val);
    outcol += 10;
}

#ifndef RELOC_BUG
PRIVATE void
put_short P1(const EXPR*, ep)
{
    put_header(wordgen, alignment(tp_short));
    putconst(ep);
    outcol += 10;
}
#endif /* RELOC_BUG */

PRIVATE void
put_pointer P1(const EXPR*, ep)
{
    put_header(longgen, alignment(tp_pointer));
    putconst(ep);
    outcol += 10;
}

PRIVATE void
put_storage P1(SYM*, sp)
{
    SIZE al = alignment(sp->tp);
    put_bseg(al);
    if (sp->storage_class == sc_static) {
	put_label(sp->value.l);
    } else
	put_name(sp);
    oprintf("\t.space\t%ld%s", sp->tp->size, newline);
}


PRIVATE void
put_literals P0(void)
/*
 * dump the string literal pool.
 * if we are producing single copies of strings (which should therefore
 * be read only we put them in the text segement - else in the data segment.
 */
{
    const CHAR	*cp;
    size_t	len;
    if (trad_option)
	put_dseg(alignment(tp_char));
    else
	put_kseg(alignment(tp_char));
    for ( ; strtab != 0; strtab = strtab->next) {
	nl();
	put_label(strtab->label);
	cp = strtab->str;
	for (len = strtab->len; len--; )
	    put_char((int)*cp++);
	put_byte(0);
	
    }
    nl();
}

/* align the following data */
static void
put_align P1(SIZE, al)
{
    nl();
    if (al > align_type) {
	if (al > 1l)
	    oprintf("\t.align\t%ld%s", al, newline);
    }
    align_type = al;
}

static void
nl P0(void)
{
    if (outcol > 0) {
	if (gentype == stringgen)
	    oprintf("\"");
	oprintf("%s", newline);
	gentype = nogen;
	outcol = 0;
    }
}

static void
seg P3(enum e_sg, segtype, const char*, segname, SIZE, al)
{
    nl();
    if (curseg != segtype) {
	oprintf("\t.sect\t%s%s", segname, newline);
	curseg = segtype;
	align_type = 0L;
    }
    put_align(al);
}

PRIVATE void
put_cseg P1(SIZE, al)
{
    seg(codeseg,".text", al);
}

PRIVATE void
put_dseg P1(SIZE, al)
{
    seg(dataseg,".data", al);
}

static void
put_bseg P1(SIZE, al)
{
    seg(bssseg,".bss", al);
}

PRIVATE void
put_kseg P1(SIZE, al)
{
    if (IandD_option)
	put_dseg(al);
    else
	put_cseg(al);
}

PRIVATE void
put_finish P0(void)
{
}

PRIVATE void
put_start P0(void)
{
    oprintf("%s Generated by %s %s %s (%s) from \"%s\"%s",
	comment, PROGNAME, VERSION, LAST_CHANGE_DATE, __DATE__, act_file,
	newline);
#ifdef VERBOSE
    {
	time_t time_of_day;
	VOIDCAST time (&time_of_day);
	oprintf("%s Compilation date/time: %s%s",
	    comment, ctime(&time_of_day), newline);
    }
#endif /* VERBOSE */
    /* introduce the sections */
    seg(codeseg,".text",(SIZE)0);
    seg(romseg,".rom",(SIZE)0);
    seg(dataseg,".data",(SIZE)0);
    seg(bssseg,".bss",(SIZE)0);
}

#ifdef MULTIPLE_ASSEMBLERS
struct funcs ack68k_funcs = {
	put_code,
	put_name,
	put_label,
	put_byte,
	put_char,
	put_word,
	put_long,
	put_pointer,
#ifndef RELOC_BUG
	put_short,
#endif /* RELOC_BUG */
	put_storage,
	put_literals,
	put_finish,
	put_start,
	put_reference,
	put_cseg,
	put_dseg,
	put_kseg,
#ifdef FLOAT_SUPPORT
	put_float,
	put_double,
	put_longdouble
#endif /* FLOAT_SUPPORT */
	};
#endif /* MULTIPLE_ASSEMBLERS */
#endif /* TARGET_ACK */
#endif /* MC680X0 */
