package org.reasm.m68k.assembly.internal;
import java.io.IOException;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;
import org.reasm.m68k.ConfigurationOptions;
import org.reasm.m68k.messages.DataForAddqSubqOutOfRangeErrorMessage;
/**
* Contains the encoding data and logic for the different forms of the <code>ADD</code>, <code>AND</code>, <code>CMP</code>
* (excluding <code>CMPM</code>), <code>EOR</code>, <code>OR</code> and <code>SUB</code> instructions.
*
* @author Francis Gagné
*/
@Immutable
class AddAndCmpEorOrSubForms {
/**
* Extends {@link AddAndCmpEorOrSubForms} to add the data and logic for the quick form of the <code>ADD</code> and
* <code>SUB</code> instructions.
*
* @author Francis Gagné
*/
@Immutable
static final class AddSubForms extends AddAndCmpEorOrSubForms {
private final int quickOpcode;
/**
* Initializes a new AddSubForms.
*
* @param baseOpcode
* the fixed bits of the base form's opcode
* @param immediateOpcode
* the fixed bits of the immediate form's opcode
* @param quickOpcode
* the fixed bits of the quick form's opcode that are specific to the instruction
*/
AddSubForms(int baseOpcode, int immediateOpcode, int quickOpcode) {
super(baseOpcode, immediateOpcode);
this.quickOpcode = 0b0101 << 12 | quickOpcode;
}
@Override
final boolean encodeQuick(M68KAssemblyContext context, InstructionSize size, EffectiveAddress ea0, EffectiveAddress ea1)
throws IOException {
return this.encodeQuick(context, size, ea0, ea1, false);
}
/**
* Encodes the quick form of an <code>ADD</code>, <code>ADDA</code> or <code>ADDI</code> (<code>ADDQ</code>) or
* <code>SUB</code> , <code>SUBA</code> or <code>SUBI</code> (<code>SUBQ</code>) instruction, if the
* {@link ConfigurationOptions#OPTIMIZE_TO_ADDQ_SUBQ} configuration option is enabled or if <code>force</code> is
* <code>true</code>.
*
* @param context
* the assembly context
* @param size
* the instruction size
* @param ea0
* the source operand
* @param ea1
* the destination operand
* @param force
* encode the quick form even if the immediate data is out of range (add an error in that case)
* @return <code>true</code> if the instruction could be encoded in the quick form, otherwise <code>false</code>
* @throws IOException
* an I/O exception occurred while encoding the instruction
*/
final boolean encodeQuick(@Nonnull M68KAssemblyContext context, @Nonnull InstructionSize size,
@Nonnull EffectiveAddress ea0, @Nonnull EffectiveAddress ea1, boolean force) throws IOException {
if (ea0.isImmediateData() && (context.optimizeToAddqSubq || force)) {
final int immediateData;
switch (size) {
case LONG:
immediateData = ea0.word1 << 16 | ea0.word2;
break;
default:
immediateData = ea0.word1;
break;
}
if (!(immediateData >= 1 && immediateData <= 8)) {
if (!force) {
return false;
}
context.addTentativeMessage(new DataForAddqSubqOutOfRangeErrorMessage());
}
// AND the immediate data with 7 to turn 8 into 0.
ea1.word0 |= (short) (this.quickOpcode | (immediateData & 7) << 9) | Mnemonic.encodeIntegerSizeStandard(size);
if (size == InstructionSize.BYTE) {
context.validateForByteAccess(ea1);
}
context.appendEffectiveAddress(ea1);
return true;
}
return false;
}
}
@Nonnull
static final AddSubForms ADD = new AddSubForms(0b1101 << 12, 0b00000110 << 8, 0b0 << 8);
@Nonnull
static final AddAndCmpEorOrSubForms AND = new AddAndCmpEorOrSubForms(0b1100 << 12, 0b00000010 << 8);
@Nonnull
static final AddAndCmpEorOrSubForms CMP = new AddAndCmpEorOrSubForms(0b1011 << 12, 0b00001100 << 8);
@Nonnull
static final AddAndCmpEorOrSubForms EOR = new AddAndCmpEorOrSubForms(0b1011 << 12 | 1 << 8, 0b00001010 << 8);
@Nonnull
static final AddAndCmpEorOrSubForms OR = new AddAndCmpEorOrSubForms(0b1000 << 12, 0b00000000 << 8);
@Nonnull
static final AddSubForms SUB = new AddSubForms(0b1001 << 12, 0b00000100 << 8, 0b1 << 8);
private final int baseOpcode;
private final int immediateOpcode;
/**
* Initializes a new AddAndCmpEorOrSubForms.
*
* @param baseOpcode
* the fixed bits of the base form's opcode
* @param immediateOpcode
* the fixed bits of the immediate form's opcode
*/
AddAndCmpEorOrSubForms(int baseOpcode, int immediateOpcode) {
this.baseOpcode = baseOpcode;
this.immediateOpcode = immediateOpcode;
}
/**
* Assembles an <code>ANDI</code> to <code>CCR</code>, <code>ANDI</code> to <code>SR</code>, <code>EORI</code> to
* <code>CCR</code>, <code>EORI</code> to <code>SR</code>, <code>ORI</code> to <code>CCR</code> or <code>ORI</code> to
* <code>SR</code> instruction.
*
* @param context
* the assembly context
* @param size
* the instruction size
* @return <code>true</code> if the destination is <code>CCR</code> or <code>SR</code> and the instruction was assembled,
* otherwise <code>false</code>
* @throws IOException
* an I/O exception occurred while encoding the instruction
*/
boolean assembleImmediateToCcrSr(@Nonnull M68KAssemblyContext context, @Nonnull InstructionSize size) throws IOException {
boolean isCcr = false;
if (Mnemonic.parseSpecialRegister(context, 1, "SR") || (isCcr = Mnemonic.parseSpecialRegister(context, 1, "CCR"))) {
final InstructionSize validSize = isCcr ? InstructionSize.BYTE : InstructionSize.WORD;
if (size != InstructionSize.DEFAULT && size != validSize) {
context.addInvalidSizeAttributeErrorMessage();
}
// Parse the source operand.
final EffectiveAddress ea = context.ea0;
context.getEffectiveAddress(context.getOperandText(0), AddressingModeCategory.IMMEDIATE_DATA, validSize, ea);
if (ea.isImmediateData()) {
ea.word0 = (short) (this.immediateOpcode | (isCcr ? 0 : 1 << 6) | EffectiveAddress.EA_IMMEDIATE_DATA);
context.appendEffectiveAddress(ea);
}
return true;
}
return false;
}
/**
* Encodes the base form of an <code>ADD</code>, <code>ADDA</code>, <code>AND</code>, <code>CMP</code>, <code>CMPA</code>,
* <code>EOR</code>, <code>OR</code>, <code>SUB</code> or <code>SUBA</code> instruction.
*
* @param context
* the assembly context
* @param size
* the instruction size
* @param ea0
* the source operand
* @param ea1
* the destination operand
* @param allowMemoryToRegister
* <code>true</code> to allow the memory-to-register encoding form, or <code>false</code> to disallow it
* @return <code>true</code> if the instruction could be encoded in the base form, otherwise <code>false</code>
* @throws IOException
* an I/O exception occurred while encoding the instruction
*/
boolean encodeBase(@Nonnull M68KAssemblyContext context, @Nonnull InstructionSize size, @Nonnull EffectiveAddress ea0,
@Nonnull EffectiveAddress ea1, boolean allowMemoryToRegister) throws IOException {
final EffectiveAddress eaOutput;
if (allowMemoryToRegister && (ea1.isDataRegisterDirect() || ea1.isAddressRegisterDirect())) {
eaOutput = ea0;
eaOutput.word0 |= this.baseOpcode | ea1.getRegister() << 9;
// Encode the opmode (which includes the instruction size).
if (ea1.isAddressRegisterDirect()) {
eaOutput.word0 |= 0b011 << 6;
switch (size) {
case BYTE:
// Byte size is invalid when address register direct is used for the destination.
context.addInvalidSizeAttributeErrorMessage();
break;
case WORD:
case DEFAULT:
default:
break;
case LONG:
eaOutput.word0 |= 0b100 << 6;
break;
}
} else {
eaOutput.word0 |= Mnemonic.encodeIntegerSizeStandard(size);
if (size == InstructionSize.BYTE) {
// Byte size is invalid when address register direct is used for the source.
context.validateForByteAccess(ea0);
}
}
} else if (ea0.isDataRegisterDirect()) {
eaOutput = ea1;
eaOutput.word0 |= this.baseOpcode | ea0.getRegister() << 9 | 0b100 << 6 | Mnemonic.encodeIntegerSizeStandard(size);
} else {
// Invalid operands: do not encode.
return false;
}
context.appendEffectiveAddress(eaOutput);
return true;
}
/**
* Encodes the immediate form of an <code>ADD</code> (<code>ADDI</code>), <code>AND</code> (<code>ANDI</code>), <code>CMP</code>
* (<code>CMPI</code>), <code>EOR</code> (<code>EORI</code>), <code>OR</code> (<code>ORI</code>) or <code>SUB</code> (
* <code>SUBI</code>) instruction.
*
* @param context
* the assembly context
* @param size
* the instruction size
* @param ea0
* the source operand
* @param ea1
* the destination operand
* @return <code>true</code> if the instruction could be encoded in the immediate form, otherwise <code>false</code>
* @throws IOException
* an I/O exception occurred while encoding the instruction
*/
boolean encodeImmediate(@Nonnull M68KAssemblyContext context, @Nonnull InstructionSize size, @Nonnull EffectiveAddress ea0,
@Nonnull EffectiveAddress ea1) throws IOException {
if (ea0.isImmediateData()) {
// ea0 contains the immediate operand. ea1 contains the destination.
ea0.word0 = ea1.word0;
ea0.word0 |= (short) this.immediateOpcode | Mnemonic.encodeIntegerSizeStandard(size);
context.appendEffectiveAddress(ea0);
context.appendEffectiveAddress(ea1, 1);
return true;
}
return false;
}
/**
* Encodes the quick form of an <code>ADD</code>, <code>ADDA</code> or <code>ADDI</code> (<code>ADDQ</code>) or <code>SUB</code>
* , <code>SUBA</code> or <code>SUBI</code> (<code>SUBQ</code>) instruction, if the
* {@link ConfigurationOptions#OPTIMIZE_TO_ADDQ_SUBQ} configuration option is enabled or if <code>force</code> is
* <code>true</code>.
*
* @param context
* the assembly context
* @param size
* the instruction size
* @param ea0
* the source operand
* @param ea1
* the destination operand
* @return <code>true</code> if the instruction could be encoded in the quick form, otherwise <code>false</code>
* @throws IOException
* an I/O exception occurred while encoding the instruction
*/
boolean encodeQuick(@Nonnull M68KAssemblyContext context, @Nonnull InstructionSize size, @Nonnull EffectiveAddress ea0,
@Nonnull EffectiveAddress ea1) throws IOException {
return false;
}
}