/*
 * 68K/386 32-bit C compiler.
 *
 * copyright (c) 1996, David Lindauer
 * 
 * This compiler is intended for educational use.  It may not be used
 * for profit without the express written consent of the author.
 *
 * It may be freely redistributed, as long as this notice remains intact
 * and sources are distributed along with any executables derived from them.
 *
 * The author is not responsible for damages, either direct or consequential,
 * that may arise from use of this software.
 *
 * V2.05 June 2002
 * David Lindauer, gclind01@starbase.spd.louisville.edu
 *
 * Credits to Mathew Brandt for original K&R C compiler
 *
 */
/*
 * iout.c
 *
 * output routines for icode code gen.  Used only in optimizer tests.
 *
 */
#include        <stdio.h>
#include        "lists.h"
#include        "expr.h"
#include        "c.h"
#include				"iexpr.h"
#include 				"diag.h"
#include				"lists.h"

/*      variable initialization         */
extern HASHREC **gsyms;

enum e_gt { nogen, bytegen, wordgen, longgen, floatgen, doublegen, longdoublegen, srrefgen };
enum e_sg { noseg, codeseg, dataseg, bssxseg,startupxseg,rundownxseg,cppxseg };

extern long nextlabel;
extern int global_flag;
extern int	prm_asmfile;
extern int prm_lines;
extern int phiused;
extern FILE *outputFile;
extern char outfile[];
extern LIST *libincludes;
extern int prm_cmangle;
extern SYM *currentfunc;

int	       gentype = nogen;		/* Current DC type */
int	       curseg = noseg;		/* Current seg */
int        outcol = 0;				/* Curront col (roughly) */
int 			 dataofs;						/* Offset from last label */
char dataname[40];						/* Name of last label */
static int phiput;

static struct slit *strtab;

/* Init module */
void outcodeini(void)
{
	gentype = nogen;
	curseg = noseg;
	outcol = 0;
	phiput = FALSE;
}
/*
 * ICODE op display handlers
 */
static void op_line(QUAD *q)
{
			if (!prm_lines)
				return;
			fprintf(outputFile,";\n; Line %d:\t%s\n;",(int)q->dc.label,(char *)q->dc.left);
}
static void op_passthrough(QUAD *q)
{
			fprintf(outputFile,"%s\n",(char *)q->dc.left);
}
static void op_label(QUAD *q)
{
		fprintf(outputFile,"L_%d:",q->dc.label);
}
static void putsingle(IMODE *ap,char *string)
{
		fprintf(outputFile,"\t%s",string);
		if (ap) {
			fputc('\t',outputFile);
			putamode(ap);
		}
}
static void op_goto(QUAD *q)
{
		fprintf(outputFile,"\tGOTO\tL_%d:PC",q->dc.label);
}
static void op_gosub(QUAD *q)
{
		putsingle(q->dc.left,"GOSUB");
}
static void op_trap(QUAD *q)
{
		putsingle(q->dc.left,"TRAP");
}
static void op_int(QUAD *q)
{
		putsingle(q->dc.left,"INT");
}
static void op_ret(QUAD *q)
{
		fprintf(outputFile,"\tRET\n");
}
static void op_rett(QUAD *q)
{
		fprintf(outputFile,"\tRETT\n");
}
static void putbin(QUAD *q, char  *str)
{
		fputc('\t',outputFile);
		putamode(q->ans);
		fprintf(outputFile," = ");
		putamode(q->dc.left);
		fprintf(outputFile," %s ",str);
		putamode(q->dc.right);
}
static void op_add(QUAD *q)
{
		putbin(q,"+");
}
static void op_sub(QUAD *q)
{
		putbin(q,"-");
}
static void op_udiv(QUAD *q)
{
		putbin(q,"U/");
}
static void op_umod(QUAD *q)
{
		putbin(q,"U%");
}
static void op_sdiv(QUAD *q)
{
		putbin(q,"S/");
}
static void op_smod(QUAD *q)
{
		putbin(q,"S%");
}
static void op_umul(QUAD *q)
{
		putbin(q,"U*");
}
static void op_smul(QUAD *q)
{
		putbin(q,"S*");
}
static void op_lsl(QUAD *q)
{
		putbin(q,"<<");
}
static void op_lsr(QUAD *q)
{
		putbin(q,"U>>");
}
static void op_asl(QUAD *q)
{
		putbin(q,"<<");
}
static void op_asr(QUAD *q)
{
		putbin(q,"S>>");
}
static void op_and(QUAD *q)
{
		putbin(q,"&");
}
static void op_or(QUAD *q)
{
		putbin(q,"|");
}
static void op_eor(QUAD *q)
{
		putbin(q,"^");
}
static void putunary(QUAD *q, char  *str)
{
		fputc('\t',outputFile);
		putamode(q->ans);
		fprintf(outputFile," = ");
		fprintf(outputFile," %s ",str);
		putamode(q->dc.left);
}
static void op_setnz(QUAD *q)
{
		putunary(q,"SETNZ");
}
static void op_neg(QUAD *q)
{
		putunary(q,"~");
}
static void op_not(QUAD *q)
{
		putunary(q,"!");
}
static void op_assn(QUAD *q)
{
		putunary(q,"");
}
static void op_genword(QUAD *q)
{
		putsingle(q->dc.left,"GENWORD");
}
static void op_coswitch(QUAD *q)
{
		fprintf(outputFile,"\tCOSWITCH(");
		putamode(q->ans);
		fputc(',',outputFile);
		putamode(q->dc.left);
		fputc(',',outputFile);
		putamode(q->dc.right);
		fputc(',',outputFile);
		fprintf(outputFile,"L_%d:PC)", q->dc.label);
}
static void op_dc(QUAD *q)
{
		fprintf(outputFile,"\tDC.L\tL_%d:PC",q->dc.label);
}
static void op_assnblock(QUAD *q)
{
		fputc('\t',outputFile);
		putamode(q->ans);
		fprintf(outputFile,"(%d) =  ",(int)q->dc.right);
		putamode(q->dc.left);
		fprintf(outputFile,"(%d)",(int)q->dc.right);
}
static void putjmp(QUAD *q,char *str)
{
		fprintf(outputFile,"\tCONDGO\tL_%d:PC ; ",q->dc.label);
		if (q->dc.left)
			putamode(q->dc.left);
		fprintf(outputFile," %s ",str);
		if (q->dc.right)
			putamode(q->dc.right);
}
static void op_jc(QUAD *q)
{
		putjmp(q,"U<");
}
static void op_ja(QUAD *q)
{
		putjmp(q,"U>");
}
static void op_je(QUAD *q)
{
		putjmp(q,"==");
}
static void op_jnc(QUAD *q)
{
		putjmp(q,"U>=");
}
static void op_jbe(QUAD *q)
{
		putjmp(q,"U<=");
}
static void op_jne(QUAD *q)
{
		putjmp(q,"!=");
}
static void op_jl(QUAD *q)
{
		putjmp(q,"S<");
}
static void op_jg(QUAD *q)
{
		putjmp(q,"S>");
}
static void op_jle(QUAD *q)
{
		putjmp(q,"S<=");
}
static void op_jge(QUAD *q)
{
		putjmp(q,"S>=");
}
static void op_parm(QUAD *q)
{
		fprintf(outputFile,"\tPARM\t");
		putamode(q->dc.left);
}
static void op_parmadj(QUAD *q)
{
		fprintf(outputFile,"\tPARMADJ\t");
		putamode(q->dc.left);
}
static void op_parmblock(QUAD *q)
{
		fputs("\tPARMBLOCK",outputFile);
		fprintf(outputFile,"{%d}=\t ",(int)q->dc.right);
		putamode(q->dc.left);
}
static void op_cppini(QUAD *q)
{
		fputs("\tCPPINI",outputFile);
}
static void op_block(QUAD *q)
{
		fprintf(outputFile,"\tBLOCK\t%d",q->dc.label+1);
}
static void op_livein(QUAD *q)
{
	DIAG("iout: propogated live-in node");
}
static void op_icon(QUAD *q)
{
		fputc('\t',outputFile);
		putamode(q->ans);
		fprintf(outputFile," = #%lX",q->dc.v.i);
}
static void op_fcon(QUAD *q)
{
		fputc('\t',outputFile);
		putamode(q->ans);
		fprintf(outputFile," = #%f",q->dc.v.f);
}
/* List of opcodes
 * This list MUST be in the same order as the op_ enums 
 */
static void (*oplst[])() = { /* NOPROTO */
	op_line, op_passthrough,
	op_label, op_goto, op_gosub, op_trap, op_int,op_ret, op_rett,
	op_add, op_sub, op_udiv, op_umod, op_sdiv, op_smod, op_umul, op_smul,
	op_lsl, op_lsr, op_asl, op_asr,
	op_and, op_or, op_eor, op_setnz, 
	op_neg, op_not,
	op_assn, op_genword, op_coswitch, op_dc, op_assnblock,
	op_jc,op_ja,op_je,op_jnc,op_jbe,op_jne,op_jl,op_jg,op_jle,op_jge,
	op_parm, op_parmadj, op_parmblock,op_cppini,op_block,op_livein,
	op_icon, op_fcon

 };
void putconst(ENODE *offset)
/*
 *      put a constant to the outputFile file.
 */
{       
				switch( offset->nodetype )
                {
								case en_icon:
												fprintf(outputFile,"%d",offset->v.i);
												break;
								case en_tempref:	
												fprintf(outputFile,"TEMP%d",((SYM *)offset->v.p[0])->value.i);
												break;
								case en_autoreg:
                        fprintf(outputFile,"%s:REG",((SYM *)offset->v.sp)->name);
												break;
                case en_autocon:
                        fprintf(outputFile,"%s:LINK",((SYM *)offset->v.sp)->name);
												break;
								case en_nalabcon:
                        fprintf(outputFile,"L_%ld:RAM",offset->v.i);
                        break;
                case en_labcon:
                        fprintf(outputFile,"L_%ld:PC",offset->v.i);
                        break;
								case en_napccon:
                        fprintf(outputFile,"%s:PC",((SYM *)offset->v.sp)->name);
                        break;
                case en_nacon:
                        fprintf(outputFile,"%s:RAM",((SYM *)offset->v.sp)->name);
                        break;
								case en_absacon:
                        fprintf(outputFile,"$%lX:ABS",((SYM *)offset->v.sp)->name);
												break;
                case en_add:
                        putconst(offset->v.p[0]);
                        fprintf(outputFile,"+");
                        putconst(offset->v.p[1]);
                        break;
                case en_sub:
                        putconst(offset->v.p[0]);
                        fprintf(outputFile,"-");
                        putconst(offset->v.p[1]);
                        break;
                case en_uminus:
                        fputc('-',outputFile);
                        putconst(offset->v.p[0]);
                        break;
                default:
                        DIAG("illegal constant node.");
                        break;
                }
}
void putlen(int l)
/*
 *      append the length field to a value
 */
{       
					switch( l )
                {
                case 0:
                        fprintf(outputFile,".0");
                        break;
                case 1:
                        fprintf(outputFile,".UB");
                        break;
								case -1:
                        fprintf(outputFile,".B");
                        break;
                case 2:
                        fprintf(outputFile,".UW");
                        break;
                case -2:
                        fprintf(outputFile,".W");
                        break;
                case 4:
                        fprintf(outputFile,".UL");
                        break;
                case -4:
                        fprintf(outputFile,".L");
                        break;
								case 6:
												fprintf(outputFile,".S");
												break;
								case 8:
												fprintf(outputFile,".D");
												break;
								case 10:
												fprintf(outputFile,".X");
												break;
                default:
                        DIAG("illegal length field.");
                        break;
                }
}

void putamode(IMODE *ap)
/*
 *      output a general addressing mode.
 */
{       
				/* We want to see if the compiler does anything wrong with
				 * volatiles
				 */
				if (ap->offset && ap->offset->cflags & DF_VOL)
					fputc('%',outputFile);
				switch( ap->mode )
                {
                case i_immed:
                        fputc('#',outputFile);
                        putconst(ap->offset);
												break;
								case i_ind:
                        fputc('*',outputFile);
                case i_direct:
                        putconst(ap->offset);
                        break;
								case i_rsp:
												fprintf(outputFile,"SP");
												break;
								case i_rret:
												fprintf(outputFile,"RV");
												break;
								case i_rlink:
												fprintf(outputFile,"LINK");
												break;
								case i_rstruct:
												fprintf(outputFile,"STRUCTRET");
												return;
								case i_rstructstar:
												fprintf(outputFile,"*STRUCTRET");
												return;
                default:
                        DIAG("illegal address mode.");
                        break;
                }
				if (ap->arrayref)
					fputc('@',outputFile);
				if (ap->ptrref)
					fprintf(outputFile,"^%02X^",ap->ptrref);
				if (ap->bits)
					fprintf(outputFile,"{%d:%d}",ap->startbit,ap->bits);
				putlen(ap->size);
}

void put_code(QUAD *q)
/*
 *      output a generic instruction.
 */
{       
		if (!prm_asmfile)	
			return;
		(*oplst[q->dc.opcode])(q);
  	fputc('\n',outputFile);
}
/*
 * Low level routinne to rewrite code for processor and dump it.
 * Here we only need dump
 */
void rewrite_icode(QUAD *q)
{
		while (q) {
			put_code(q);
			q = q->fwd;
		}
}

void gen_strlab(char *s)
/*
 *      generate a named label.
 */
{
		sprintf(dataname,"%s",s);
		if (prm_asmfile)
       fprintf(outputFile,"%s:\n",dataname);
		dataofs = 0;
}

void put_label(int lab)
/*
 *      outputFile a compiler generated label.
 */
{
		sprintf(dataname,"L_%d",lab);
       if (prm_asmfile)
					fprintf(outputFile,"%s:\n",dataname);
		dataofs = 0;
}
void put_staticlabel(long label)
{
	put_label(label);
}

void genfloat(float val)
/*
 * Output a float value
 */
{ 		if (prm_asmfile)
        if( gentype == floatgen && outcol < 60) {
                fprintf(outputFile,",%f",val);
                outcol += 8;
                }
        else    {
                nl();
                fprintf(outputFile,"\tDC.S\t%f",val);
                gentype = floatgen;
                outcol = 19;
                }
	dataofs+=4;
}

void gendouble(double val)
/*
 * Output a double value
 */
{ 		if (prm_asmfile)
        if( gentype == doublegen && outcol < 60) {
                fprintf(outputFile,",%f",val);
                outcol += 8;
                }
        else    {
                nl();
                fprintf(outputFile,"\tDC.D\t%f",val);
                gentype = doublegen;
                outcol = 19;
                }
	dataofs+=8;
}
void genlongdouble(long double val)
/*
 * Output a double value
 */
{ 		if (prm_asmfile)
        if( gentype == longdoublegen && outcol < 60) {
                fprintf(outputFile,",%f",val);
                outcol += 8;
                }
        else    {
                nl();
                fprintf(outputFile,"\tDC.X\t%f",val);
                gentype = longdoublegen;
                outcol = 19;
                }
	dataofs+=8;
}

void genbyte(long val)
/*
 * Output a byte value
 */
{ 		if (prm_asmfile)
        if( gentype == bytegen && outcol < 60) {
                fprintf(outputFile,",$%X",val & 0x00ff);
                outcol += 4;
                }
        else    {
                nl();
                fprintf(outputFile,"\tDC.B\t$%X",val & 0x00ff);
                gentype = bytegen;
                outcol = 19;
                }
	dataofs+=1;
}

void genword(long val)
/*
 * Output a word value
 */
{     if (prm_asmfile)
        if( gentype == wordgen && outcol < 58) {
                fprintf(outputFile,",$%X",val & 0x0ffff);
                outcol += 6;
                }
        else    {
                nl();
                fprintf(outputFile,"\tDC.W\t$%X",val & 0x0ffff);
                gentype = wordgen;
                outcol = 21;
                }
	dataofs+=2;
}

void genlong(long val)
/*
 * Output a long value
 */
{     if (prm_asmfile)
        if( gentype == longgen && outcol < 56) {
                fprintf(outputFile,",$%lX",val);
                outcol += 10;
                }
        else    {
                nl();
                fprintf(outputFile,"\tDC.L\t$%lX",val);
                gentype = longgen;
                outcol = 25;
                }
	dataofs+=4;
}
void genaddress(char *string)
/*
 * Output a long value
 */
{     if (prm_asmfile)
        if( gentype == longgen && outcol < 56) {
                fprintf(outputFile,",%s",string);
                outcol += strlen(string);
                }
        else    {
                nl();
                fprintf(outputFile,"\tDC.L\t%s",string);
                gentype = longgen;
                outcol = 25;
                }
	dataofs+=4;
}
void gensrref(char *name,int val)
/*
 * Output a startup/rundown reference
 */
{
			if (prm_asmfile)
        if( gentype == srrefgen && outcol < 56) {
                fprintf(outputFile,",%s,%d",name,val);
                outcol += strlen(name)+1;
                }
        else    {
                nl();
                fprintf(outputFile,"\tDC.L\t%s,%d",name,val);
                gentype = srrefgen;
                outcol = 25;
                }
}
void genref(SYM *sp,int offset)
/*
 * Output a reference to the data area (also gens fixups )
 */
{       char    sign;
			char buf[40];
        if( offset < 0) {
                sign = '-';
                offset = -offset;
                }
        else
                sign = '+';
			sprintf(buf,"%s%c%d",sp->name,sign,offset);
			if (prm_asmfile) {
        if( gentype == longgen && outcol < 55 - strlen(sp->name)) {
                fprintf(outputFile,",%s",buf);
                outcol += (11 + strlen(sp->name));
                }
        else    {
                nl();
                fprintf(outputFile,"\tDC.L\t%s",buf);
                outcol = 26 + strlen(sp->name);
                gentype = longgen;
                }
			}
	dataofs+=4;
}
void genpcref(SYM *sp,int offset)
/*
 * Output a reference to the code area (also gens fixups )
 */
{       char    sign;
				char buf[40];
        if( offset < 0) {
                sign = '-';
                offset = -offset;
                }
        else
                sign = '+';
			sprintf(buf,"%s%c%d",sp->name,sign,offset);
			if (prm_asmfile) {
        if( gentype == longgen && outcol < 55 - strlen(sp->name)) {
                fprintf(outputFile,",%s",buf);
                outcol += (11 + strlen(sp->name));
                }
        else    {
                nl();
                fprintf(outputFile,"\tDC.L\t%s",buf);
                outcol = 26 + strlen(sp->name);
                gentype = longgen;
                }
			}
	dataofs+=4;
}

void genstorage(int nbytes)
/*
 * Output bytes of storage
 */
{			if (prm_asmfile) {
        nl();
        fprintf(outputFile,"\tDS.B\t$%X\n",nbytes);
			}
	dataofs+=nbytes;
}

void gen_labref(int n)
/*
 * Generate a reference to a label
 */
{			if (prm_asmfile)
        if( gentype == longgen && outcol < 58) {
                fprintf(outputFile,",L_%d",n);
                outcol += 6;
                }
        else    {
                nl();
                fprintf(outputFile,"\tDC.L\tL_%d",n);
                outcol = 22;
                gentype = longgen;
                }
	dataofs+=4;
}

int     stringlit(char *s)
/*
 *      make s a string literal and return it's label number.
 */
{       struct slit     *lp;
        ++global_flag;          /* always allocate from global space. */
        lp = xalloc(sizeof(struct slit));
        lp->label = nextlabel++;
        lp->str = litlate(s);
        lp->next = strtab;
        strtab = lp;
        --global_flag;
        return lp->label;
}

void dumplits(void)
/*
 *      dump the string literal pool.
 */
{       char            *cp;
        while( strtab != 0) {
                cseg();
                nl();
                put_label(strtab->label);
                cp = strtab->str;
                while(*cp)
                        genbyte(*cp++);
                genbyte(0);
                strtab = strtab->next;
                }
        nl();
}

void nl(void)
/*
 * New line
 */
{    if (prm_asmfile) {
       if(outcol > 0) {
                fputc('\n',outputFile);
                outcol = 0;
                gentype = nogen;
                }
			 if (phiused && !phiput)
								fputc(0x1f,outputFile);
		 }
}
/*
 * Switch to cseg 
 */
void cseg(void)
{			if (prm_asmfile)
       	if( curseg != codeseg) {
                nl();
                fprintf(outputFile,"\tSECTION\tcode\n");
                curseg = codeseg;
                }
}
/*
 * Switch to dseg
 */
void dseg(void)
{     if (prm_asmfile)  
				if( curseg != dataseg) {
                nl();
                fprintf(outputFile,"\tSECTION\tdata\n");
                curseg = dataseg;
                }
}
/*
 * Switch to bssseg
 */
void bssseg(void)
{     if (prm_asmfile)  
				if( curseg != bssxseg) {
                nl();
                fprintf(outputFile,"\tSECTION\tbss\n");
                curseg = bssxseg;
                }
}
/*
 * Switch to startupseg
 */
void startupseg(void)
{     if (prm_asmfile)  
				if( curseg != startupxseg) {
                nl();
                fprintf(outputFile,"\tSECTION\tcstartup\n");
                curseg = startupxseg;
                }
}
/*
 * Switch to rundownseg
 */
void rundownseg(void)
{     if (prm_asmfile)  
				if( curseg != rundownxseg) {
                nl();
                fprintf(outputFile,"\tSECTION\tcrundown\n");
                curseg = rundownxseg;
                }
}
/*
 * Switch to cppseg
 */
void cppseg(void)
{     if (prm_asmfile)  
				if( curseg != cppxseg) {
                nl();
                fprintf(outputFile,"\tSECTION\tcppinit\n");
                curseg = cppxseg;
                }
}
void gen_virtual(char *name)
{
	if (prm_asmfile) {
		nl();
		fprintf(outputFile,"@%s\tVIRTUAL",name);
	}
}
void gen_endvirtual(char *name)
{
	if (prm_asmfile) {
		nl();
		fprintf(outputFile,"@%s\tENDVIRTUAL",name);
	}
}
void asm_header(void)
{
	fprintf(outputFile,";Icode test - %s\n\n",outfile);
}
void globaldef(SYM *sp)
{
	char buf[100],*q=buf,*p=sp->name;
	if (curseg == codeseg && currentfunc->pascaldefn) {
		if (prm_cmangle)
			p++;
		while(*p)
			*q++=toupper(*p++);
		*q++ = 0;
	}
	else
		strcpy(buf,p);
  fprintf(outputFile,"\tPUBLIC\t%s\n",buf);
}			
void output_alias(SYM *sp)
{
	char *name;
#ifdef CPLUSPLUS
	name = sp->fullname;
	if (!name)
#endif
		name = sp->name;
  fprintf(outputFile,"%s EQU\t<%s>\n",name,sp->alias);
}                       
void putexterns(void)
/*
 * Output the fixup tables and the global/external list
 */
{       SYM     *sp;
			int i;
			if (prm_asmfile){
						int notyet = TRUE;
				nl();
				for (i=0; i < HASHTABLESIZE; i++) {
					if ((sp=(SYM *) gsyms[i]) != 0) {
						while (sp) {
    	    		if( sp->storage_class == sc_externalfunc && sp->extflag) {
								char buf[100],*q=buf,*p=sp->name;
								if (curseg == codeseg && sp->pascaldefn) {
									if (prm_cmangle)
										p++;
									while(*p)
										*q++=toupper(*p++);
									*q++ = 0;
								}
								else
								strcpy(buf,p);
							 	if (notyet) {
									fprintf(outputFile,"\n\t.CODE\n");
									notyet = FALSE;
								}
      	        fprintf(outputFile,"\tEXTRN\t%s:PROC\n",buf);
							}
         			sp = sp->next;
						}
					}
				}
				notyet = TRUE;
				for (i=0; i < HASHTABLESIZE; i++) {
					if ((sp=(SYM *) gsyms[i]) != 0) {
						while (sp) {
	        		if( sp->storage_class == sc_external && sp->extflag) {
								char buf[100],*q=buf,*p=sp->name;
								if (curseg == codeseg && sp->pascaldefn) {
									if (prm_cmangle)
										p++;
									while(*p)
										*q++=toupper(*p++);
									*q++ = 0;
								}
								else
									strcpy(buf,p);
								if (notyet) {
									fprintf(outputFile,"\n\t.DATA\n");
									notyet = FALSE;
								}
  	            fprintf(outputFile,"\tEXTRN\t%s\n",buf);
							}
         			sp = sp->next;
						}
					}
				}
				if (libincludes) {
					while (libincludes) {
						fprintf(outputFile,"\tINCLUDELIB\t%s\n",libincludes->data);
						libincludes = libincludes->link;
					}
					fputc('\n',outputFile);
				}
				fprintf(outputFile,"\tEND\n");
			}
}
