/********************************************************/
/*							*/
/*		Q/C Compiler Version 3.2		*/
/*		    (Part 7 for Z80)			*/
/*							*/
/*     Copyright (c) 1984 Quality Computer Systems	*/
/*							*/
/*			01/28/84			*/
/********************************************************/

#include "stdio.h"
#include "cstddef.h"
#include "cglbdecl.c"

/* This section contains all code generation routines
 * which generate mnemonics for specific assemblers
 */

/* Names of Z80 registers used */
static char    *z80reg[] = {"HL", "DE", "BC", "IX", "IY"};

/* Print assembler info before any code is generated */
header()
{
    outstr(";Compiled by Q/C ");
    outstr(version);
    outstr(" for Z280");
    nl();
    ol(".Z80");
    ol("INCLUDE\t280MACRO.MAC");
}

/* Generate code to be done before main() */
shell(argcnt)
{
    static char    *func;
    char            hex[5];

    char           *itob();

    codeseg();
    glblabel("_main", SC_GLOBAL);	/* establish entry point */
    if (romflag)
    {
	ot("LD\tSP,0");		/* stack addr from -S switch */
	outstr(itob(spaddr, hex, 16));
	outbyte('H');
	nl();
    } else
    {
	ol("LD\tSP,(6)");	/* start stack below CP/M BDOS */
	if (redirect)		/* full redirection requested */
	    func = "_rshell";
	else if (argcnt)	/* parse CP/M command line */
	    func = "_shell";
	else
	{
	    ccall("main");
	    func = "_exit";
	}
	ccall(func);
	declext(func);
    }
}

/* Print any assembler stuff needed after all code */
trailer()
{
    static int      n;
    static struct st *p;
    static struct typeinfo *t;
    static char     extrns[][57] = {
	"EXTRN\t?o,?x,?a,?e,?ne",
	"EXTRN\t?gt,?lt,?le,?ge,?ugt,?ult,?ule,?uge,?asr,?asr1",
	"EXTRN\t?asl,?asl1,?lsr,?lsr1,?com,?n,?div",
	"EXTRN\t?udiv,?sw,?nen,?nens,?nexs,?nenss,?nexss"
    };

	for (p = symtab; p < symtab + nsym; ++p)
	{
	    if (!p->st_type)
		continue;	/* Skip empty entries */
	    if (p->st_sc == SC_EXTERN && p->st_decl == DECL_GLB)
		declext(p->st_name);
	    t = p->st_type;
	    if (t->t_code == T_STRUCT && t->t_size == 0
		&& p->st_sc == SC_TYPE)
		tagerr(p->st_name);
	}
	ol(extrns[0]);
	ol(extrns[1]);
	ol(extrns[2]);
	ol(extrns[3]);
	ot("END");
	if (mainflag)
	{
	    outbyte('\t');	/* define entry point */
	    outname("_main");
	}
	nl();
}

/* Comment line for the assembler */
comment()
{
    newline();			/* start new line if necessary */
    outbyte(';');
}

/* "#asm" preprocessor command */
doasm()
{
    register        c;

    if (match("8080"))
    {				/* look for 8080 compatibility request */
	newline();
	ol(".8080");
    }
    cmode = FALSE;		/* this is not C */
    newline();			/* start a new line if necessary */
    for (;;)
    {
	inline();		/* get next line */
	if (ch() == ';')
	    continue;		/* discard comment line */
	if (eof)
	    break;
	if (ch() == '#')
	{
	    junk();		/* skip to next alphanumeric */
	    if (amatch("endasm"))
		break;
	}
	while (c = gch())
	{
	    if (c == ';')
		break;		/* strip comments */
	    outbyte(c);
	}
	nl();
    }
    kill_line();		/* clear #endasm line */
    ol(".Z80");
    cmode = TRUE;		/* back in C mode */
    if (trace.is_on)		/* if TRACE was requested */
	retvalue = TRUE;	/* force compiler to set Z flag */
}


/* Load global/static variable into a register */
getmem(op, reg)
    register struct operand *op;
    int             reg;
{
    register        offset;
    register struct st *sym;

    offset = op->op_val;
    sym = op->op_sym;
    if (op->op_type->t_size == 1)
    {				/* load 1 byte */
	if (reg == SREG)
	    return (FALSE);	/* Can't handle sugn extension */
	ot("LD\tA,(");
	prtsymname(sym, offset);
	outbyte(')');
	nl();
	ot("EXTS");
	testflag = REGL;
    } else
    {				/* all other types are 2 bytes in V3 */
	if (reg == PREG)
	{
	    ot("LD\tHL,(");
	    prtsymname(sym, offset);
	    outbyte(')');
	} else
	{
	    ot("LDDED\t");
	    prtsymname(sym, offset);
	}
    }
    prtname(sym);
    return (TRUE);
}



/* Load an auto variable */
autoload(op, reg)
    struct operand *op;
    int             reg;
{
    register struct st *sym;
    register        offset, size;

    size = op->op_type->t_size;
    sym = op->op_sym;
    offset = sym->st_info + op->op_val;
    if (size > 1)
    {
	if (reg == PREG)
	    ot("LDHLX\t");
	else
	{
   	    if (offset > -127 && offset < 127)
	        ot("LDDEX\t");
	    else
		return (FALSE);
	}
	outdec(offset);
    } else
    {
	if (reg != PREG)
	    return (FALSE);	/* Can't do this */
	if (offset > -127 && offset < 127)
	{
	    ot("LD\tA,");
	    xreg(offset);
	} else
	{
	    ot("LDAIX\t");
	    outdec(offset);
	}
	nl();
	ot("EXTS");
	testflag = REGL;
    }
    prtname(sym);		/* print name as comment */
    return (TRUE);
}


regload(op, reg)
    struct operand *op;
    int             reg;
{

    if (op->op_type->t_size != 2)
	generr();

    if (op->op_sym->st_info == 1)
    {
	/* Load from BC  */
	if (reg == PREG)
	{
	    ol("LD\tH,B");
	    ot("LD\tL,C");
	} else
	{
	    ol("LD\tD,B");
	    ot("LD\tE,C");
	}
    } else
    {

	/* Load from IY */
	if (reg == PREG)
	{
	    ol("LDHIYH");
	    ot("LDLIYL");
	} else
	{
	    ol("LDDIYH");
	    ot("LDEIYL");
	}
    }
    prtname(op->op_sym);	/* print name as comment */
    return (TRUE);
}






/* Load the address of op in the reg */
getaddr(op, reg)
    register struct operand *op;
    int             reg;
{
    register struct st *sym;
    register        offset;

    sym = op->op_sym;
    if (sym == NULL) {
	if (reg == SREG)
	    copy_reg();
	return (TRUE);		/* address already loaded */
    }

    offset = op->op_val;	/* constant offset from sym */
    if (sym->st_sc == SC_AUTO)
    {
	if (reg != PREG)
	    return (FALSE);	/* Can't do this */
	offset += sym->st_info;	/* offset of sym from stk frame */
	ot("LDADRX\t");
	outdec(offset);
	prtname(sym);
    } else
    {				/* address of global or static */
	if (reg == PREG)
	    ot("LD\tHL,");
	else
	    ot("LD\tDE,");
	prtsymname(sym, offset);
	prtname(sym);
    }
    op->op_sym = NULL;		/* record that preg has address */
    op->op_load &= ~LOADADDR;
    if (reg == PREG)
	pregflag = TRUE;
    return (TRUE);
}


/* Store specified size from preg to address on top of stack */
/* If keep is true, leave the value in preg */
putstk(size, keep)
    int             size;
    int             keep;
{
    cpop(SREG);
    if (size == 1)
    {
	ol("LD\tA,L");
	ol("LD\t(DE),A");
	testflag = REGL;
    } else
    {				/* all other types in Q/C V3 are 2 bytes */
	swap_reg();
	ol("LDMDE");		/* LD (HL), DE  */
	if (keep)
	    swap_reg();
    }
}

/* Store preg in the specified memory location */
putmem(op)
    register struct operand *op;
{
    register struct st *sym;
    register        offset, size;

    offset = op->op_val;	/* constant offset from sym */
    size = op->op_type->t_size;
    sym = op->op_sym;
    if (sym->st_sc == SC_AUTO)
    {
	offset += sym->st_info;	/* offset of sym from stk frame */
	if (size == 1)
	{
	    if (offset > -127 && offset < 127)
	    {
		ot("LD\t");
		xreg(offset);
		outstr(",L");
	    } else
	    {
		ol("LD\tA,L");
		ot("STAIX\t");
		outdec(offset);
		testflag = REGL;
	    }
	} else
	{
	    ot("STHLX\t");
	    outdec(offset);
	}
	prtname(sym);		/* print name as comment */
    } else if (sym->st_sc == SC_REG)
    {
	if (size == 1)
	    generr();		/* Register vars should be word */

	if (sym->st_info == 1)
	{
	    ol("LD\tB,H");	/* Register 1 is BC */
	    ot("LD\tC,L");
	} else
	{
	    ol("LDIYHH");	/* Register 2 is IY */
	    ot("LDIYLL");
	}
	prtname(sym);		/* print name as comment */

    } else
    {
	if (size == 1)
	{
	    ol("LD\tA,L");
	    ot("LD\t(");
	    prtsymname(sym, offset);
	    outstr("),A");
	    testflag = REGL;
	} else
	{
	    ot("LD\t(");
	    prtsymname(sym, offset);
	    outstr("),HL");
	}
	prtname(sym);
    }
    peepflag = PREGSYM;		/* remember the variable in preg */
    strcpy(peepsym, sym->st_name);
    peepoffset = op->op_val;
    peeplen = size;
}

/* Print external or internal symbol name */
prtsymname(sym, offset)
    register struct st *sym;
    int             offset;
{
    switch (sym->st_sc)
    {
    case SC_GLOBAL:
    case SC_EXTERN:
    case SC_ST_GLB:
	outname(sym->st_name);
	break;
    case SC_STATIC:
	printlabel(sym->st_info);
	break;
    default:
	generr();
    }
    if (offset)
    {
	if (offset > 0)
	    outbyte('+');
	outdec(offset);
    }
}

/* Print comment if wanted */
prtcomm(s)
    char           *s;
{
    if (fullist)
    {
	ot("; ");
	outstr(s);
	nl();
    }
}

/* Print symbol name as comment */
prtname(sym)
    struct st      *sym;
{
    if (fullist)
    {
	ot(";");		/* prt extra .asm info */
	outstr(sym->st_name);
    }
    nl();
}

/* Print index register plus offset as address */
xreg(offset)
    int             offset;
{
    outstr("(IX");
    if (offset >= 0)
	outstr("+");
    outdec(offset);
    outstr(")");
}


/* Swap preg and sreg */
swap_reg()
{
    ol("EX\tDE,HL");
}

copy_reg()
{
    ol("LD\tD,H");
    ol("LD\tE,L");
}

limmed(val, reg)
    int             val, reg;
{
    ot("LD");
    ot(z80reg[reg - PREG]);
    outbyte(',');
    outdec(val);
    nl();
}


/* Load immediate into memory byte specified by preg */
ldmemi(value)
{
    ot("LD\t(HL),");
    outdec(value);
    nl();
}

set1()
{
    ol("LD\tHL,1");
}

/* Push reg onto stack */
cpush(reg)
    int             reg;
{
    ot("PUSH");
    ol(z80reg[reg - PREG]);
    ++stklvl;
}

/* Pop top of the stack into reg */
cpop(reg)
    int             reg;
{
    ot("POP");
    ol(z80reg[reg - PREG]);
    --stklvl;
}

/* Restore stack frame at end of function */
restsfp()
{
    cpop(XREG);
}

/* Swap preg and top of the stack */
swapstk()
{
    ol("EX\t(SP),HL");
}

/* Output the name of the specified library routine */
libname(sname)
    char           *sname;
{
    struct st      *findloc(), *addloc();
    static char     name[7] = {ASMRTS};

    strcpy(name + 1, sname);
    ol(name);
}

/* Call the specified library routine */
callib(sname)
    char           *sname;
{
    ot("CALL");
    libname(sname);
}

/* Call the specified subroutine name */
ccall(sname)
    char           *sname;
{
    ot("CALL\t");
    outname(sname);
    nl();
}

/* Perform subroutine call to value on top of stack */
callstk()
{
    cpop(PREG);
    ol("CALLM");
}

/* Jump to specified internal label number */
jump(label)
    int             label;
{
    switch (peepflag)
    {				/* check for peephole optimization */
    case JUMP:			/* last line also JMP, do nothing */
	break;
    case JUMPTRUE:		/* 2nd part of	JNZ/JZ	jcondlabel */
    case JUMPFALSE:		/* JMP	jumplabel */
	/* jcondlabel:			  */
	if (jumplabel != 0)	/* if last line also a JMP */
	    break;		/* skip this one */
	loadpeep("\tJP\t");	/* save this part of pattern */
	peeplabel(jumplabel = label);
	break;
    default:
	dumpeep();		/* dump some other pending pattern */
    case FALSE:
	ot("JP\t");
	printlabel(label);
	nl();
	peepflag = JUMP;	/* remember this is start of pattern: */
	break;			/* JMP	label1		 */
    }				/* JMP	label2		 */
}

/* Remember that this is start of a pattern consisting of
 *		JP Z/NZ jcondlabel
 *		JP	jumplabel
 * jcondlabel:
 */
jumpcond(type, label)
    int             type, label;
{
    dumpeep();			/* dump any pending pattern */
    if (!testdone)		/* did we just test (e.g. $eq)? */
	dotest();		/* if not, generate a test */
    if (type == FALSE)
    {
	peepflag = JUMPFALSE;
	loadpeep("\tJP\tZ,");
    } else
    {
	peepflag = JUMPTRUE;
	loadpeep("\tJP\tNZ,");
    }
    peeplabel(jcondlabel = label);
    jumplabel = 0;		/* will be set if 2nd part of pattern found */
}

/* Generate a test of the preg */
dotest()
{
    switch (testflag)
    {				/* see what we need to do test */
    case REGL:
	ol("OR\tH");		/* L already in A */
	return;
    default:			/* need full test */
	ol("LD\tA,H");
    case REGH:			/* H already in A */
	ol("OR\tL");
    case TESTED:		/* zero/non-zero test already done */
	return;
    }
}

/* Print a line label */
linelabel(label)
    int             label;
{
    char           *jump;

    if ((peepflag == JUMPTRUE || peepflag == JUMPFALSE)	/* 1st part? */
	&& jumplabel != 0	/* 2nd part of pattern found? */
	&& label == jcondlabel)
    {				/* now check for final part */
	jump = (peepflag == JUMPTRUE) ? "JP\tZ," : "JP\tNZ,";
	clearpeep();		/* clear pattern buffer */
	ot(jump);
	printlabel(jumplabel);
	nl();
	return;
    }
    newline();			/* start new line if necessary */
    printlabel(label);
    outbyte(':');
}

/* Print a global label */
glblabel(name, sc)
    char           *name;
    int             sc;
{
    if (sc == SC_GLOBAL)
    {
	ot("PUBLIC\t");
	outname(name);
	nl();
    }
    outname(name);
    outbyte(':');
}

/* Data segment */
dataseg()
{
    if (segtype != DATA)
    {
	newline();		/* start new line if necessary */
	ol("DSEG");
	segtype = DATA;
    }
}

/* Code segment */
codeseg()
{
    if (segtype != CODE)
    {
	newline();
	ol("CSEG");
	segtype = CODE;
    }
}

/* Declare external */
declext(name)
    char           *name;
{
    ot("EXTRN\t");
    outname(name);
    nl();
}

/* Default initialization of a global or static variable */
definit(amt)
    register        amt;
{
    if (amt <= 0)
	return 0;
    newline();			/* start new line */
    switch (amt)
    {
    default:
	ot("REPT\t");
	outdec(amt);
	nl();
    case 1:
	defbyte();
	goto value;
    case 2:
	defword();
value:	outbyte('0');
	nl();
	if (amt > 2)
	    ol("ENDM");
    }
    return amt;
}

/* pseudo-op to define a byte */
defbyte()
{
    ot("DEFB\t");
}

/* pseudo-op to define storage */
defstorage()
{
    ot("DEFS\t");
}

/* pseudo-op to define a word */
defword()
{
    ot("DEFW\t");
}

/* pseudo-op to define an equality */
equ()
{
    ot("EQU\t");
}

/* clear amount requested off the stack */
clearstk(amt)
    int             amt;
{
    switch (amt)
    {
    case 8:
	cpop(SREG);		/* special case for 8 bytes or less */
    case 6:
	cpop(SREG);
    case 4:
	cpop(SREG);
    case 2:
	cpop(SREG);
    case 0:
	return;
    default:
	swap_reg();		/* save possible return value */
	limmed(amt, PREG);
	ol("ADD\tHL,SP");
	ol("LD\tSP,HL");
	swap_reg();		/* restore return value */
	stklvl -= (amt/2);
	return;
    }
}

/* Double the preg */
doublereg()
{
    ol("ADD\tHL,HL");
}

/* Add preg and sreg */
cadd()
{
    ol("ADD\tHL,DE");
}

/* Increment preg */
cinc()
{
    ol("INC\tHL");
}

/* Decrement preg */
cdec()
{
    ol("DEC\tHL");
}

/* Return from subroutine */
cret()
{
    ol("RET");
}



/* end of CC7.C */
