/*
 *      do11.c	-	MC68HC11 specific processing
 *
 *	From Motorola BBS.
 *	See ../doc/ChangeLog to examine and/or add change descriptions.
 */

#define PAGE1   0x00
#define PAGE2   0x18
#define PAGE3   0x1A
#define PAGE4   0xCD

/* addressing modes */
#define IMMED   0
#define INDX    1
#define INDY    2
#define LIMMED  3       /* long immediate */
#define OTHER   4

int     yflag = 0;      /* YNOIMM, YLIMM, and CPD flag */

/*
 *      localinit --- machine specific initialization
 */
void
localinit(void)
{
}

/*
 *      do_op --- process mnemonic
 *
 *	Called with the base opcode and it's class. Optr points to
 *	the beginning of the operand field.
 */
void
do_op(int opcode, int class)
{
	char *skip_white(char *ptr);
	char c;
	int     dist;   /* relative branch distance */
	int     amode;  /* indicated addressing mode */
	char	*peek;

	if ( class == INH ) { /* inherent addressing */
			emit(opcode);
			return;
		}
	else if ( class == P2INH ) { /* inherent addr with prebyte */
			emit(PAGE2);
			return;
		}
	/* guess at addressing mode */
	peek = Optr;
	amode = OTHER;
	if( head(Operand,"X") || head(Operand,"x") ) {
		if( delim(*(Operand+1)) )
			amode == INDX;
		}
	else if( head(Operand,"Y") || head(Operand,"y") ) {
		if( delim(*(Operand+1)) )
			amode == INDY;
		}
	else while( !delim(*peek) ) { /* check for comma in operand field */
		if( *peek == ',' ){
			if ( (peek != Optr) && ((*(peek-1) == '\\') || (*(peek-1) == '\'' )) ) {
				peek++;	/* skip quoted comma or literal comma */
				continue;
			}
			else {
				peek++;
				if( mapdn(*peek) == 'y' )
					amode = INDY;
				else {
					amode = INDX;
					if( mapdn(*peek) != 'x' )
						error("Must use X or Y register");
					}
				break;
				}
			}
			else
				peek++;
		}
	if( *Optr == '#' ) amode = IMMED;

	yflag = 0;
	switch(class){
		case REL:                       /* relative branches */
			if( eval() )
				dist = Result - (Pc+2);
			else dist = -2;
			emit(opcode);
			if( (dist >127 || dist <-128) && Pass==2){
				error("Branch out of Range");
				dist = -2;
				}
			emit(lobyte(dist));
			return;
		case LONGIMM:
			if( amode == IMMED )
				amode = LIMMED;
		case NOIMM:
			if( amode == IMMED ){
				error("Immediate Addressing Illegal");
				return;
				}
		case GEN:                       /* general addressing */
			do_gen11(opcode,amode,PAGE1,PAGE1,PAGE2);
			return;
		case GRP2:
			if( amode == INDY ){
				Cycles++;
				emit(PAGE2);
				amode = INDX;
				}
			if( amode == INDX )
				do_indexed(opcode);
			else{   /* extended addressing */
				eval();
				emit(opcode+0x10);
				eword(Result);
				}
			return;
		case CPD:               /* cmpd */
			if( amode == IMMED )
				amode = LIMMED;
			if( amode == INDY )
				yflag=1;
			do_gen11(opcode,amode,PAGE3,PAGE3,PAGE4);
			return;
		case XNOIMM:            /* stx */
			if( amode == IMMED ){
				error("Immediate Addressing Illegal");
				return;
				}
		case XLIMM:             /* cpx, ldx */
			if( amode == IMMED )
				amode = LIMMED;
			do_gen11(opcode,amode,PAGE1,PAGE1,PAGE4);
			return;
		case YNOIMM:            /* sty */
			if( amode == IMMED ){
				error("Immediate Addressing Illegal");
				return;
				}
		case YLIMM:             /* cpy, ldy */
			if(amode == INDY)
				yflag=1;
			if( amode == IMMED )
				amode = LIMMED;
			do_gen11(opcode,amode,PAGE2,PAGE3,PAGE2);
			return;
		case SETCLR:               /* bset, bclr */

			/* TBD: THESE CAN TAKE 2 OPERANDS */

		case BTB:            /* brset, brclr */

			/* TBD: THESE CAN TAKE 3 OPERANDS */
			/* TBD: Must have flag to flip expr2 <-> expr3 */

			opcode = bitop(opcode,amode,class);

			if (amode == INDX)
				Cycles++;
			else if( amode == INDY ){
				Cycles+=2;
				emit(PAGE2);
				amode = INDX;
				}
			emit(opcode);
			Result = 0;	/* setup in case eval() not called */
			Force_byte = 0;
			Force_word = 0;
			c = mapdn(*Optr);
			if( c != ',' ) {
			   if( c != 'x' && c != 'y' || !delim(*(Optr+1)) ) {
				eval();
				if( amode == INDX )
				   if( *Optr++ != ',')
				      error("Missing comma");
				}
			   }
			else Optr++;
			if( Result < 0 || Result > 255)
				warn("Offset Value Truncated (low byte used)");

			emit(lobyte(Result));   /* address */
			if( amode == INDX )
				Optr++;      /* skip x or y */

			Optr = skip_white(Optr);
			eval();
			if( (unsigned int)Result > 0xFF )
				error("Bit mask more than 8 bits");
			emit(lobyte(Result));   /* mask */
			if( class == SETCLR )
				return;
			Optr = skip_white(Optr);
			if( eval() )
				dist = Result - (Pc+1);
			else dist = Old_pc - (Pc + 1);
			if( (dist >127 || dist <-128) && Pass==2){
				error("Branch out of Range");
				dist = Old_pc - (Pc+1);
				}
			emit(lobyte(dist));	/* rel dist */
			return;
		default:
			fatal("Error in Mnemonic table");
		}
}

/*
 *      bitop --- adjust opcode on bit manipulation instructions
 */
bitop(int op, int mode, int class)
{
	if( mode == INDX || mode == INDY )
		return(op);
	if( class == SETCLR )
		return(op-8);
	else if(class==BTB)
		return(op-12);
	else
		fatal("Assembler error in subroutine bitop()");
}

/*
 *      do_gen --- process general addressing modes
 */
void
do_gen11(int op, int mode, int pnorm, int px, int py)
{
	switch(mode){
	case LIMMED:
		Optr++;
		epage(pnorm);
		emit(op);
		eval();
		eword(Result);
		break;
	case IMMED:
		Optr++;
		epage(pnorm);
		emit(op);
		eval();
		emit(lobyte(Result));
		break;
	case INDY:
		if(yflag)
			Cycles += 2;
		else
			Cycles += 3;
		epage(py);
		do_indexed(op+0x20);
		break;
	case INDX:
		Cycles+=2;
		epage(px);
		do_indexed(op+0x20);
		break;
	case OTHER:
		eval();
		epage(pnorm);
		if(Force_word){
			emit(op+0x30);
			eword(Result);
			Cycles+=2;
			break;
			}
		if(Force_byte){
			emit(op+0x10);
			emit(lobyte(Result));
			Cycles++;
			break;
			}
		if(Result>=0 && Result <=0xFF){
			emit(op+0x10);
			emit(lobyte(Result));
			Cycles++;
			break;
			}
		else {
			emit(op+0x30);
			eword(Result);
			Cycles+=2;
			break;
			}
		break;
	default:
		error("Unknown Addressing Mode");
	}
}

/*
 *      do_indexed --- handle all wierd stuff for indexed addressing
 */
void
do_indexed(int op)
{
	char c;

	emit(op);
	Result = 0;		/* setup in case eval() not called */
	Force_byte = 0;
	Force_word = 0;
	c = mapdn(*Optr);
	if( c != ',' ) {
		if( c != 'x' && c != 'y' || !delim(*(Optr+1)) ) {
			eval();
			if( *Optr++ != ',' )
			error("Missing comma");
			}
		}
	else Optr++;
	c = mapdn(*Optr++);
	if( c != 'x' && c != 'y')
		warn("Indexed Addressing Assumed");
	if( Result < 0 || Result > 255)
		warn("Value Truncated");
	emit(lobyte(Result));
}

/*
 *      epage --- emit page prebyte
 */
epage(int p)
{
	if( p != PAGE1 )        /* PAGE1 means no prebyte */
		emit(p);
}
