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

/* addressing modes */
#define IMMED   0       /* immediate */
#define IND     1       /* indexed */
#define OTHER   2       /* NOTA */

/*
 *      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);
	int     dist;   /* relative branch distance */
	int     amode;  /* indicated addressing mode */
	char	*peek;

	if ( class == INH ) { /* inherent addressing */
			emit(opcode);
			return;
		}
	/* guess at addressing mode */
	peek = Optr;
	amode = OTHER;
	if( head(Operand,"X") || head(Operand,"x") ) {
		if( delim(*(Operand+1)) )
			amode = IND;
		}
	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++;
					amode = IND;
					break;
				}
			}
			else
				peek++;
		}
	if( *Optr == '#' ) amode = IMMED;

	switch(class){
		case GEN:                       /* general addressing */
			do_gen(opcode,amode);
			return;
		case REL:                       /* short 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 NOIMM:
			if( amode == IMMED ){
				error("Immediate Addressing Illegal");
				return;
				}
			do_gen(opcode,amode);
			return;
		case GRP2:
			if( amode == IND ){
				do_indexed(opcode+0x20);
				return;
				}
			eval();
			Cycles += 1;
			if(Force_byte){
				emit(opcode);
				emit(lobyte(Result));
				return;
				}
			if(Result>=0 && Result <=0xFF){
				emit(opcode);
				emit(lobyte(Result));
				return;
				}
			error("Extended Addressing not allowed");
			return;
		case SETCLR:
		case BTB:
			eval();
			if(Result <0 || Result >7){
				error("Bit Number must be 0-7");
				return;
				}
			emit( opcode | (Result << 1));
			if(*Optr++ != ',') error("Missing comma");
			Optr = skip_white(Optr);
			eval();
			emit(lobyte(Result));
			if(class==SETCLR)
				return;
			/* else it's bit test and branch */
			if(*Optr++ != ',') error("Missing comma");
			Optr = skip_white(Optr);
			if( eval() )
				dist = Result - (Old_pc+3);
			else dist = -3;
			if( (dist >127 || dist <-128) && Pass==2){
				error("Branch out of Range");
				dist = -3;
				}
			emit(lobyte(dist));
			return;
		default:
			fatal("Error in Mnemonic table");
		}
}

/*
 *      do_gen --- process general addressing
 */
void
do_gen(int op, int mode)
{
	if( mode == IMMED){
		Optr++;
		emit(op);
		eval();
		emit(lobyte(Result));
		return;
		}
	else if( mode == IND ){
		do_indexed(op+0x30);
		return;
		}
	else if( mode == OTHER){        /* direct or extended addressing */
		eval();
		if(Force_word){
			emit(op+0x20);
			eword(Result);
			Cycles += 2;
			return;
			}
		if(Force_byte){
			emit(op+0x10);
			emit(lobyte(Result));
			Cycles += 1;
			return;
			}
		if(Result >= 0 && Result <= 0xFF){
			emit(op+0x10);
			emit(lobyte(Result));
			Cycles += 1;
			return;
			}
		else {
			emit(op+0x20);
			eword(Result);
			Cycles += 2;
			return;
			}
		}
	else {
		error("Unknown Addressing Mode");
		return;
		}
}

/*
 *      do_indexed --- handle all weird stuff for indexed addressing
 */
void
do_indexed(int op)
{
	if( *Optr != ',' && !head(Operand,"X") && !head(Operand,"x") ) {
		eval();
	        if( !( *Optr++ == ',' && ( mapdn(*Optr) == 'x') ) )
			warn("Indexed Addressing Assumed");
		}
	else {			/* if no expression, fake it */
		Force_word = NO;
		Force_byte = NO;
		Result = 0;
		if( *Optr == ',' ) Optr++;
		if( mapdn(*Optr) != 'x' )
			warn("Indexed Addressing Assumed");
		}
	if(Force_word){
		if(op < 0x80 ){ /* group 2, no extended addressing */
			emit(op+0x10); /* default to one byte indexed */
			emit(lobyte(Result));
			Cycles += 2;
			return;
			}
		emit(op);
		eword(Result);
		Cycles += 3;
		return;
		}
	Cycles += 2;    /* assume 1 byte indexing */
	if(Force_byte){
		emit(op+0x10);
		emit(lobyte(Result));
		return;
		}
	if(Result==0){
		emit(op+0x20);
		Cycles--;       /* ,x slightly faster */
		return;
		}
	if(Result>0 && Result <=0xFF){
		emit(op+0x10);
		emit(lobyte(Result));
		return;
		}
	if( op < 0x80 ){
		warn("Address Offset Value Truncated");
		emit(op+0x10);
		emit(lobyte(Result));
		return;
		}
	emit(op);
	eword(Result);
	Cycles++;       /* 2 byte slightly slower */
	return;
}
