package org.reasm.m68k.assembly.internal;
import java.io.IOException;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;
/**
* The <code>MOVE</code> instruction.
*
* @author Francis Gagné
*/
@Immutable
class MoveInstruction extends TwoOperandIntegerInstruction {
@Nonnull
static final MoveInstruction MOVE = new MoveInstruction();
static void assembleBasicMove(@Nonnull M68KAssemblyContext context, @Nonnull InstructionSize size,
@Nonnull Set<AddressingMode> validAddressingModesForDestination) throws IOException {
final EffectiveAddress ea0 = context.ea0;
final EffectiveAddress ea1 = context.ea1;
// Parse and evaluate the operands.
context.getEffectiveAddress(context.getOperandText(0), AddressingModeCategory.ALL, size, 2, ea0);
context.getEffectiveAddress(context.getOperandText(1), validAddressingModesForDestination, size, ea0.numberOfWords * 2, ea1);
// Try to optimize to MOVEQ.
if (context.optimizeMoveToMoveq && size == InstructionSize.LONG && ea0.isImmediateData() && ea1.isDataRegisterDirect()) {
final int immediateData = ea0.word1 << 16 | ea0.word2;
if (immediateData >= -0x80 && immediateData <= 0x7F) {
MoveqInstruction.encode(context, immediateData, ea1.getRegister());
return;
}
}
// Encode the instruction.
switch (size) {
case BYTE:
ea0.word0 |= 0b00010000_00000000;
// Byte size is invalid when address register direct is used for the source and/or the destination.
context.validateForByteAccess(ea0);
context.validateForByteAccess(ea1);
break;
case LONG:
ea0.word0 |= 0b00100000_00000000;
break;
case WORD:
default:
ea0.word0 |= 0b00110000_00000000;
break;
}
ea0.word0 |= (short) (ea1.getRegister() << 9);
ea0.word0 |= (short) (ea1.getMode() << 3);
context.appendEffectiveAddress(ea0);
context.appendEffectiveAddress(ea1, 1);
}
private static void assembleMoveSrCcr(@Nonnull M68KAssemblyContext context, int otherOperandIndex,
@Nonnull InstructionSize size, boolean isCcr, boolean isDest) throws IOException {
final EffectiveAddress ea = context.ea0;
// Parse and evaluate the operand that is not the CCR or SR register.
context.getEffectiveAddress(context.getOperandText(otherOperandIndex), isDest ? AddressingModeCategory.DATA
: AddressingModeCategory.ALTERABLE, InstructionSize.WORD, ea);
// Encode the instruction.
ea.word0 |= (short) (0b01000000_11000000 | (isDest ? 0b00000100_00000000 : 0) | (isCcr ^ isDest ? 0b00000010_00000000 : 0));
context.appendEffectiveAddress(ea);
// Yes, MOVE from/to CCR is a word-sized operation. Go figure.
if (size != InstructionSize.DEFAULT && size != InstructionSize.WORD) {
context.addInvalidSizeAttributeErrorMessage();
}
}
private static void assembleMoveUsp(@Nonnull M68KAssemblyContext context, int otherOperandIndex, @Nonnull InstructionSize size,
boolean isDest) throws IOException {
final EffectiveAddress ea = context.ea0;
// Parse and evaluate the operand that is not the USP register.
context.getEffectiveAddress(context.getOperandText(otherOperandIndex), AddressingModeCategory.ADDRESS_REGISTER_DIRECT,
InstructionSize.LONG, ea);
// Encode the instruction.
final int register;
register = ea.isAddressRegisterDirect() ? ea.word0 & EffectiveAddress.REGISTER_MASK : 0;
ea.word0 = (short) (0b01001110_01100000 | (isDest ? 0 : 0b00000000_00001000) | register);
ea.numberOfWords = 1;
context.appendEffectiveAddress(ea);
if (size != InstructionSize.DEFAULT && size != InstructionSize.LONG) {
context.addInvalidSizeAttributeErrorMessage();
}
}
private MoveInstruction() {
}
@Override
void assemble(M68KAssemblyContext context, InstructionSize size) throws IOException {
boolean isCcr = false;
boolean isDest = false;
if (parseSpecialRegister(context, 0, "SR") || (isCcr = parseSpecialRegister(context, 0, "CCR"))
|| (isDest = parseSpecialRegister(context, 1, "SR")) || (isCcr = isDest = parseSpecialRegister(context, 1, "CCR"))) {
assembleMoveSrCcr(context, isDest ? 0 : 1, size, isCcr, isDest);
} else if (parseSpecialRegister(context, 0, "USP") || (isDest = parseSpecialRegister(context, 1, "USP"))) {
assembleMoveUsp(context, isDest ? 0 : 1, size, isDest);
} else {
assembleBasicMove(context, size, AddressingModeCategory.ALTERABLE);
}
}
}