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

#define IMMED   0
#define IND     1
#define OTHER   2

/* special addresses */
#define XREG    0x80
#define YREG    0x81
#define SD1REG  0x82
#define SD2REG  0x83
#define ACCUM   0xFF

/*
 *      localinit --- machine specific initialization
 */
void
localinit(void)
{
	install("x",XREG);
	install("X",XREG);
	install("y",YREG);
	install("Y",YREG);
	install("a",ACCUM);
	install("A",ACCUM);
}

/*
 *      do_op --- process mnemonic
 */
void
do_op(int opcode, int class)
{
	char *skip_white(char *ptr);
	int     dist;   /* relative branch distance */
	int     amode;  /* indicated addressing mode */
	int     r1;     /* first eval() for mvi */

	if ( class == INH ) { /* inherent addressing */
			emit(opcode);
			return;
		}
	if (( *Operand == '[' ) || ( *Operand == ','))
		amode = IND;
	else if( *Operand == '#' )
		amode = IMMED;
	else
		amode = OTHER;

	switch(class){
		case APOST:             /* A address in mem follows opcode */
			emit(opcode);
			emit(ACCUM);
			return;
		case REL:                       /* short relative branches */
			if( eval() )
				dist = Result - (Pc+1);
			else dist = -1;
			if( (dist >15 || dist <-16) && Pass==2){
				error("Branch out of Range");
				dist = -1;
				}
			emit(opcode + (dist&0x1F));
			return;
		case BTB:
		case SETCLR:
			eval();
			if(Result <0 || Result >7){
				error("Bit Number must be 0-7");
				return;
				}
			emit( opcode + Result);
			if(*Optr++ != ',')error("Missing comma");
			Optr = skip_white(Optr);
			eval();
			emit(lobyte(Result));
			if( class == SETCLR )
				return;
			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;
		case EXT:               /* jsr, jmp */
			eval();
			emit(opcode | (hibyte(Result) & 0x0F));
			emit(lobyte(Result));
			return;
		case BPM:       /* brset/clr 7,accum,target */
			emit(opcode);
			emit(ACCUM);
			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;
		case MVI:
			eval();
			r1 = Result;    /* save result */
			if (*Optr++ != ',')
				warn("Missing comma");
			Optr = skip_white(Optr);
			eval();
			mvi(opcode,r1,Result);
			return;
		case CLRX:      /* mvi xreg,0 */
			mvi(opcode,XREG,0);
			return;
		case CLRY:      /* mvi yreg,0 */
			mvi(opcode,YREG,0);
			return;
		case LDX:       /* mvi xreg data */
			if (amode == IMMED) Optr++;
			eval();
			mvi(opcode,XREG,Result);
			return;
		case LDY:       /* mvi yreg data */
			if (amode == IMMED) Optr++;
			eval();
			mvi(opcode,YREG,Result);
			return;
		case NOIMM:
			if( amode == IMMED ){
				error("Immediate Addressing Illegal");
				return;
				}
		case GEN:
			if ( amode == IMMED ) {
				Optr++;
				eval();
				emit(opcode | 0x08);
				emit(lobyte(Result));
				return;
				}
			if( amode == IND ){
				Optr++;
				eval();
				if ((*Optr != ']') && (*Operand != ','))
					warn("Missing ']'");
				if (Result != XREG && Result != YREG) {
				   error("Operand must be X or Y register");
				   emit(opcode);
				   return;
				}
				emit(opcode | ((Result&0x01)<<4));
				return;
				}
			eval();
			if (XREG <= Result && Result <=SD2REG){
				/*check for short direct cases*/
				if ( opcode==0xE6 ) {   /* inc */
					emit(0xA8 + (Result-XREG));
					return;
					}
				if ( opcode==0xE7 ) { /* dec */
					emit(0xB8 + (Result-XREG));
					return;
					}
				if ( opcode==0xE0 ) { /* lda */
					emit(0xAC | (Result-XREG));
					return;
					}
				if ( opcode==0xE1 ) { /* sta */
					emit(0xBC | (Result-XREG));
					return;
					}
				}
			/* else direct addressing */
			emit( opcode | 0x18);
			emit(lobyte(Result));
			return;
		default:
			fatal("Error in Mnemonic table");
		}
}

mvi(int op, int to, int from)
{
	emit(op);
	emit(to);
	emit(from);
}
