/* assemble.c - main loop for assembler */

#include "syshead.h"
#include "const.h"
#include "type.h"
#include "address.h"
#include "globvar.h"
#include "opcode.h"
#include "scan.h"

PRIVATE bool_t nocolonlabel;	/* set for labels not followed by ':' */
PRIVATE void (*routine) P((void));
#ifdef I80386
PRIVATE opcode_t rep = 0;       /* which rep/repne prefix was seen */
#endif
PRIVATE pfv rout_table[] =
{
    pelse,
    pelseif,
    pelsifc,
    pendif,
    pif,
    pifc,

    /* start of non-conditionals */
    palign,
    pasciz,
    pblkw,
    pblock,
    pbss,
    pcomm,
    pcomm1,
    pdata,
    pendb,
    penter,
    pentry,
    pequ,
    peven,
    pexport,
    pfail,
    pfcb,
    pfcc,
    pfdb,
#if SIZEOF_OFFSET_T > 2
    pfqb,
#endif
    pget,
    pglobl,
    pident,
    pimport,
    plcomm,
    plcomm1,
    plist,
    ploc,
    pmaclist,
    pmacro,
    pmap,
    porg,
    pproceof,
    prmb,
    psect,
    pset,
    psetdp,
    ptext,
#ifdef I80386
    puse16,
    puse32,
#endif
    pwarn,
    /* end of pseudo-ops */

#ifdef I80386
    mbcc,
    mbswap,
    mcall,
    mcalli,
    mdivmul,
    menter,
    mEwGw,
    mExGx,
    mf_inher,
    mf_m,
    mf_m2,
    mf_m2_ax,
    mf_m2_m4,
    mf_m2_m4_m8,
    mf_m4_m8_optst,
    mf_m4_m8_st,
    mf_m4_m8_stst,
    mf_m4_m8_m10_st,
    mf_m10,
    mf_optst,
    mf_st,
    mf_stst,
    mf_w_inher,
    mf_w_m,
    mf_w_m2,
    mf_w_m2_ax,
    mgroup1,
    mgroup2,
    mgroup6,
    mgroup7,
    mgroup8,
    mGvEv,
    mGvMa,
    mGvMp,
    mimul,
    min,
    mincdec,
    minher,
    minher16,
    minher32,
    minhera,
    mint,
    mjcc,
    mjcxz,
    mlea,
    mmov,
    mmovx,
    mnegnot,
    mout,
    mpushpop,
    mret,
    mseg,
    msetcc,
    mshdouble,
    mtest,
    mxchg,
#endif /* I80386 */

#ifdef MC6809
    mall,			/* all address modes allowed, like LDA */
    malter,			/* all but immediate, like STA */
    mimmed,			/* immediate only (ANDCC, ORCC) */
    mindex,			/* indexed (LEA's) */
    minher,			/* inherent, like CLC or CLRA */
    mlong,			/* long branches */
    mshort,			/* short branches */
    msstak,			/* S-stack	(PSHS, PULS) */
    mswap,			/* TFR, EXG */
    mustak,			/* U-stack	(PSHU,PULU) */
#endif /* MC6809 */
};

FORWARD void asline P((void));

/*
  This uses registers as follows: A is for work and is not preserved by
  the subroutines.B holds the last symbol code, X usually points to data
  about the last symbol, U usually holds the value of last expression
  or symbol, and Y points to the current char. The value in Y is needed
  by READCH and GETSYM.  EXPRES needs B and Y, and returns a value in U.
  If the expression starts with an identifier, X must point to its string.
  LOOKUP needs a string pointer in X and length in A. It returns a table
  pointer in X (unless not assembling and not found), symbol type in A
  and overflow in CC.
*/

PUBLIC void assemble()
{
    while (TRUE)
    {
	asline();
	if (label != NUL_PTR)	/* must be confirmed if still set */
	{			/* it is nulled by EQU,	COMM and SET */
#ifndef MC6809
#define NEEDENDLABEL ILLAB
	    if (nocolonlabel)
		error(NEEDENDLABEL);
#endif
	    if(pass && label->value_reg_or_op.value != oldlabel)
	    {
	       dirty_pass = TRUE;
	       if( pass == last_pass )
	          error(UNSTABLE_LABEL);
            }

	    label->type |= LABIT;	/* confirm, perhaps redundant */
	    if (label->type & REDBIT)
	    {
		/* REDBIT meant 'GLOBLBIT' while LABIT was not set. */
		label->type |= EXPBIT;
		label->type &= ~REDBIT;
	    }
	    if ((mcount | popflags) == 0)
		/* unaccompanied label, display adr like EQU and SET */
		showlabel();
	    label = NUL_PTR;	/* reset for next line */
	}
        skipline();
	listline();
	genbin();
	genobj();
	binmbuf = lc += lcjump
#ifdef I80386
	    + immcount
#endif
	    ;
    }
}

PRIVATE void asline()
{
    register struct sym_s *symptr;

    postb = popflags = pcrflag =
#ifdef I80386
	sprefix = oprefix = aprefix =
#endif
	immcount = lastexp.data = lcjump = 0;
#ifdef I80386
    sib = NO_SIB;
#endif
#if SIZEOF_OFFSET_T > 2
    fqflag =
#endif
	fdflag = fcflag = FALSE;
    cpuwarn();
    readline();
    getsym();
    if (sym != IDENT)		/* expect label, mnemonic or macro */
    {	
       /* Warn if not a comment marker or a hash (for /lib/cpp) */
       if( sym != EOLSYM && sym != IMMEDIATE )
          list_force = TRUE;
       return;			/* anything else is a comment */
    }
    symptr = gsymptr;
    if (!ifflag)
	/* not assembling, just test for IF/ELSE/ELSEIF/ENDIF */
    {
	if (symptr == NUL_PTR || !(symptr->type & MNREGBIT) ||
	    symptr->data & REGBIT ||
	    symptr->value_reg_or_op.op.routine >= MIN_NONCOND)
	    return;
    }
    else if (!(symptr->type & (MACBIT | MNREGBIT)))
	/* not macro, op, pseudo-op or register, expect label */
    {
	oldlabel = symptr->value_reg_or_op.value;

	if ((nocolonlabel = (*lineptr - ':')) == 0)	/* exported label? */
	{
	    sym = COLON;
	    ++lineptr;
	}
	if (symptr->type & (LABIT | VARBIT))
	{
	    if (symptr->type & REDBIT)
		labelerror(RELAB);
	    label = symptr;

	    if (pass && !(symptr->type & VARBIT) /*&& last_pass>1*/)
	    {
	       label->data = (label->data & FORBIT) | lcdata;
	       label->value_reg_or_op.value = lc;
	    }
	}
	else if (checksegrel(symptr))
	{
	    symptr->type &= ~COMMBIT;	/* ignore COMM, PCOMM gives warning */
#ifdef MC6809
#if 0
	    if (sym == COLON)
		symptr->type |= EXPBIT;
#endif
#endif
				/* remember if forward referenced */
	    symptr->data = (symptr->data & FORBIT) | lcdata;
	    symptr->value_reg_or_op.value = lc;
				/* unless changed by EQU,COMM or SET */
	    label = symptr;
	}

	getsym();
	if (sym != IDENT)
	{
	    if (sym == EQOP)
	    {
		getsym();
		pequ();
	    }
	    return;		/* anything but ident is comment */
	}
	symptr = gsymptr;
    }
    if (symptr->type & MACBIT)
    {
	entermac(symptr);
	return;
    }
    if (!(symptr->type & MNREGBIT))
    {
	error(OPEXP);
	return;
    }
    if (symptr->data & REGBIT)
    {
	error(REGUID);
	return;
    }
    mnsize = 0;
    if ((page = (symptr->data & (PAGE1 | PAGE2))) != 0)
    {
#ifdef MNSIZE
	if (page == (PAGE1 | PAGE2))
	{
	    mnsize = 1;
	    page = 0;
	}
	else
#endif
	{
#ifdef PAGE2_OPCODE
	    if (page == PAGE2)
		page = PAGE2_OPCODE;
	    else
#endif
		page = PAGE1_OPCODE;
	    mcount = 1;
	}
    }
    opcode = symptr->value_reg_or_op.op.opcode;
#ifdef I80386
    needcpu((page==0 && ((opcode&0xF0) == 0x60||(opcode&0xF6)==0xC0))?1:0);
#endif
    routine = rout_table[symptr->value_reg_or_op.op.routine];
    getsym();
    (*routine)();
#ifdef I80386
    /* We handle "rep[ne]" refix as separate instruction; check if its use is valid */
    if (opcode == 0xF2 || opcode == 0xF3) {    /* REP */
        rep = opcode;
    /* Not another prefix */
    } else if (opcode != 0x2E &&    /* CSEG */
        opcode != 0x3E &&           /* DSEG */
        opcode != 0x26 &&           /* ESEG */
        opcode != 0x64 &&           /* FSEG */
        opcode != 0x65 &&           /* GSEG */
        opcode != 0x36 &&           /* SSEG */
        opcode != 0xF0) {           /* LOCK */
        if (rep == 0xF2 && (opcode&0xF6) != 0xA6)       /* REPNE CMPS/SCAS */
            error (REPNE_STRING);
        if (rep == 0xF3 && !((opcode&0xFC) == 0x6C ||   /* REP INS/OUTS */
            (opcode&0xFC) == 0xA4 ||                    /* REP MOVS/CMPS */
            (opcode&0xFC) == 0xAC ||                    /* REP SCAS/LODS */
            (opcode&0xFE) == 0xAA))                     /* REP STOS */
            error (REP_STRING);
        rep = 0;
    }
#endif
    if (sym != EOLSYM)
	error(JUNK_AFTER_OPERANDS);
#ifdef I80386
    needcpu(page==PAGE1_OPCODE?2:0);

    if (aprefix != 0)
	++mcount;
    if (oprefix != 0)
	++mcount;
    if (sprefix != 0)
	++mcount;
#endif
}
