branch(brtyp, link, val)
word_t brtyp;
word_t link;
valu_t val;
{
	valu_t offset;

	offset = val - DOTVAL - 8;
	if ((offset & 0xFC000000) != 0 && (offset & 0xFC000000) != 0xFC000000){
		serror("offset out of range");
	}
	offset = offset>>2 & 0xFFFFFF; 
	emit4(brtyp|link|offset);
	return;
}

data(opc, ins, val, typ)
word_t opc, ins;
valu_t val;
short typ;
{
	valu_t tmpval;

	if (typ == S_REG){
		emit4(opc|ins|val);
		return;
	}

	ins |= 0x02000000;

	tmpval = val;
	if (typ == S_ABS){
		if (calcimm(&opc, &tmpval, typ)){
			emit4(opc|ins|tmpval);
			return;
		}
	}

	tmpval = val;
	if (small(calcimm(&opc, &tmpval, typ),12)){
		emit4(opc|ins|tmpval);
		return;
	}	

	switch (opc){
	case MOV:
		if (small((val & 0xF0000000) == 0xF0000000, 8)){
			emit4(0xE51F0004 | (ins & 0xF000));
			emit4(val);
			return;
		}
		if (small(1,4)){
			emit4(0xE51F0000 | (ins & 0xF000));
			emit4(0xEA000000);
			emit4(val);
			return;
		}
		DOTVAL += 16;
		return;
	case ADD:
		if (small((val & 0xF0000000) == 0xF0000000, 4)){
			emit4(0xE51F0004 | (ins & 0xF000));
			emit4(val);
			emit4(0xE2800000 | (ins&0xFF000) | (ins&0xF000)>>12);
			return;
		}
		emit4(0xE51F0000 | (ins & 0xF000));
		emit4(0xEA000000);
		emit4(val);
		emit4(0xE2800000 | (ins&0xFF000) | (ins&0xF000)>>12);
		return;
	default:
		if (pass == PASS_1)
			DOTVAL += 16;
		else
			serror("immediate value out of range");
		return;
	}
}

calcimm(opc,val,typ)
word_t *opc;
valu_t *val;
short typ;
{
	int i = 0;

	if (typ == S_UND) return 0;

	if ((*val & ~0xFF) == 0) return 1;

	if ((~*val & ~0xFF) == 0){
		switch (*opc){
		case AND:
			*val = ~*val;
			*opc = BIC;
			return 1;
		case MOV:
			*val = ~*val;
			*opc = MVN;
			return 1;
		case ADC:
			*val = ~*val;
			*opc = SBC;
			return 1;
		default : 
			break;
		}
	}	
	if ((-1**val & ~0xFF) == 0 ){
		switch (*opc){
		case ADD:
			*val *= -1;
			*opc = SUB;
			return 1;
		case CMP:
			*val *= -1;
			*opc = CMN;
			return 1;
		default:
			break;
		}
	}

	do{
		rotateleft2(&*val);
		i++;
		if((*val & ~0xFF) == 0){
			*val = *val|i<<8;
			return 1;
		}
		if ((~*val & ~0xFF) == 0){
			switch (*opc){
			case AND:
				*val = ~*val|i<<8;
				*opc = BIC;
				return 1;
			case MOV:
				*val = ~*val|i<<8;
				*opc = MVN;
				return 1;
			case ADC:
				*val = ~*val|i<<8;
				*opc = SBC;
				return 1;
			default : 
				break;
			}
		}	
	}while(i<15);

	return 0;
}

word_t
calcoffset(val)
valu_t val;
{
	if((val & ~0xFFF) == 0)
		return(val|0x00800000);
	val *= -1;
	if((val & ~0xFFF) == 0)
		return(val);
	serror("offset out of range");
	return(0);
}

word_t
calcaddress(val,typ,reg)
valu_t val;
short typ;
word_t reg;
{
	int tmpval;

	if (typ == S_UND){
		DOTVAL += 8;
		return 0;
	}
	tmpval = val - DOTVAL - 8;
	if(small((tmpval & ~0xFFF) == 0, 8))
		return(val|0x008F0000);
	tmpval *= -1;
	if(small((tmpval & ~0xFFF) == 0, 8))
		return(val|0x000F0000);
	emit4(0xE51F0004 | reg << 12);
	emit4(val | 0xF0000000);
	return(reg << 16);
}

word_t
calcadr(reg, val, typ)
word_t reg;
valu_t val;
short typ;
{
	valu_t tmpval = val;
	int i = 0;

	if ((val & 0xFC000000) && (typ != S_UND)){
		serror("address out of range");
		return 0;
	}

	if (typ != S_ABS){
		tmpval = val-DOTVAL-8;
		if (small((tmpval & ~0xFF) == 0),12){
			emit4(0xE2000000|ADD|0xF<<16|reg<<12|tmpval);
			return 0;
		}
	
		tmpval *= -1;
		if (small((tmpval & ~0xFF) == 0), 12){
			emit4(0xE2000000|SUB|0xF<<16|reg<<12|tmpval);
			return 0;
		}
	}

	data(MOV, 0xE2000000||reg<<12, val, typ);

	return 0;
}


word_t
calcshft(val, typ, styp)
valu_t val;
short typ;
word_t styp;
{
	if (typ=S_UND) return 0;
	if (val & ~0x1F) serror("shiftcount out of range");
	if (styp && !val) warning("shiftcount 0");
	return((val & 0x1F)<<7);
}

rotateleft2(x)
long *x;
{
	unsigned long bits;

	bits = *x & 0xC0000000;
	*x <<= 2 ;
	if (bits){
		bits >>= 30;
		*x |= bits;
	}
	return;
}
