package org.reasm.m68k.assembly.internal;
import java.io.IOException;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;
import org.reasm.commons.messages.AddressingModeNotAllowedHereErrorMessage;
import org.reasm.commons.source.LogicalLineReader;
import org.reasm.m68k.messages.DivisionRemainderDiscardedWarningMessage;
import org.reasm.m68k.messages.MultiplicationResultUndefinedWarningMessage;
/**
* The <code>DIVS</code>, <code>DIVSL</code>, <code>DIVU</code>, <code>DIVUL</code>, <code>MULS</code> and <code>MULU</code>
* instructions.
*
* @author Francis Gagné
*/
@Immutable
class MultiplyDivideInstruction extends TwoOperandIntegerInstruction {
@Immutable
private enum Operation {
MULX, DIVX, DIVXL
}
@Nonnull
static final MultiplyDivideInstruction DIVS = new MultiplyDivideInstruction(Operation.DIVX, 1);
@Nonnull
static final MultiplyDivideInstruction DIVSL = new MultiplyDivideInstruction(Operation.DIVXL, 1);
@Nonnull
static final MultiplyDivideInstruction DIVU = new MultiplyDivideInstruction(Operation.DIVX, 0);
@Nonnull
static final MultiplyDivideInstruction DIVUL = new MultiplyDivideInstruction(Operation.DIVXL, 0);
@Nonnull
static final MultiplyDivideInstruction MULS = new MultiplyDivideInstruction(Operation.MULX, 1);
@Nonnull
static final MultiplyDivideInstruction MULU = new MultiplyDivideInstruction(Operation.MULX, 0);
@Nonnull
private final Operation operation;
private final int signed;
private MultiplyDivideInstruction(@Nonnull Operation operation, int signed) {
this.operation = operation;
this.signed = signed;
}
@Override
void assemble(M68KAssemblyContext context, InstructionSize size) throws IOException {
final EffectiveAddress ea = context.ea0;
// Parse the source operand.
context.getEffectiveAddress(context.getOperandText(0), AddressingModeCategory.DATA, size, ea);
// Parse the destination operand.
boolean errorInDestination = false;
context.prepareOperandReader(1);
final LogicalLineReader reader = context.logicalLineReader;
GeneralPurposeRegister firstRegister = parseRegister(context, reader);
if (firstRegister == null || firstRegister.compareTo(GeneralPurposeRegister.D7) > 0) {
errorInDestination = true;
firstRegister = GeneralPurposeRegister.D0;
}
GeneralPurposeRegister secondRegister = null;
reader.skipWhitespace();
if (!reader.atEnd() && reader.getCurrentCodePoint() == (this.operation == Operation.MULX ? '-' : ':')) {
reader.advance();
reader.skipWhitespace();
secondRegister = parseRegister(context, reader);
if (secondRegister == null || secondRegister.compareTo(GeneralPurposeRegister.D7) > 0) {
errorInDestination = true;
secondRegister = GeneralPurposeRegister.D0;
}
}
if (!reader.atEnd()) {
errorInDestination = true;
}
if (size == InstructionSize.WORD) {
if (secondRegister != null) {
errorInDestination = true;
}
ea.word0 |= 0b10000000_11000000 | (this.operation == Operation.MULX ? 1 << 14 : 0) | (firstRegister.ordinal() & 7) << 9
| this.signed << 8;
context.appendEffectiveAddress(ea);
checkInstructionSet(InstructionSetCheck.M68000_FAMILY, context);
} else {
if (secondRegister == null && this.operation == Operation.DIVXL) {
errorInDestination = true;
}
int firstRegisterNumber = firstRegister.ordinal() & 7;
int secondRegisterNumber = firstRegisterNumber;
int operationSize = 0;
if (secondRegister != null) {
secondRegisterNumber = secondRegister.ordinal() & 7;
if (this.operation != Operation.DIVXL) {
operationSize = 1 << 10;
}
if (!errorInDestination && firstRegisterNumber == secondRegisterNumber) {
if (this.operation == Operation.MULX) {
context.addTentativeMessage(new MultiplicationResultUndefinedWarningMessage());
} else {
context.addTentativeMessage(new DivisionRemainderDiscardedWarningMessage());
}
}
}
context.appendWord((short) (0b01001100_00000000 | (this.operation == Operation.MULX ? 0 : 1 << 6) | ea.word0));
context.appendWord((short) (secondRegisterNumber << 12 | this.signed << 11 | operationSize | firstRegisterNumber));
context.appendEffectiveAddress(ea, 1);
checkInstructionSet(InstructionSetCheck.CPU32_OR_MC68020_OR_LATER, context);
}
if (errorInDestination) {
context.addTentativeMessage(new AddressingModeNotAllowedHereErrorMessage());
}
}
@Override
void checkInstructionSet(M68KAssemblyContext context) {
// Don't check now.
}
@Override
InstructionSize getInstructionSize(M68KAssemblyContext context) {
InstructionSize size = super.getInstructionSize(context);
InstructionSize defaultSize;
if (this.operation == Operation.DIVXL) {
defaultSize = InstructionSize.LONG;
} else {
defaultSize = InstructionSize.WORD;
}
if (size == InstructionSize.DEFAULT) {
size = defaultSize;
} else if (size != InstructionSize.LONG && (this.operation == Operation.DIVXL || size != InstructionSize.WORD)) {
context.addInvalidSizeAttributeErrorMessage();
size = defaultSize;
}
return size;
}
}