package org.reasm.m68k.assembly.internal;
import java.io.IOException;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;
import org.reasm.SymbolContext;
import org.reasm.SymbolType;
import org.reasm.UnsignedIntValue;
import org.reasm.Value;
import org.reasm.ValueToBooleanVisitor;
import org.reasm.expressions.BinaryOperator;
/**
* The <code>FOR</code> directive.
*
* @author Francis Gagné
*/
@Immutable
class ForDirective extends Mnemonic {
@Nonnull
static final ForDirective FOR = new ForDirective();
@Nonnull
private static final Value ZERO = new UnsignedIntValue(0);
@Nonnull
private static final Value ONE = new UnsignedIntValue(1);
private static void defineVariable(@Nonnull M68KAssemblyContext context, @Nonnull String label, @CheckForNull Value counter) {
context.defineSymbol(SymbolContext.VALUE, label, SymbolType.VARIABLE, counter);
}
private ForDirective() {
}
@Override
void assemble(M68KAssemblyContext context) throws IOException {
context.sizeNotAllowed();
final Object blockState = context.getParentBlock();
if (!(blockState instanceof ForBlockState)) {
throw new AssertionError();
}
final ForBlockState forBlockState = (ForBlockState) blockState;
// The FOR directive is assembled on every iteration.
// On the first iteration, evaluate the operands and store information to use on subsequent iterations.
if (!forBlockState.parsed) {
if (context.numberOfLabels == 0) {
forBlockState.labels = null;
} else if (context.numberOfLabels == 1) {
forBlockState.labels = context.getLabelText(0);
} else {
final String[] labels = new String[context.numberOfLabels];
for (int i = 0; i < context.numberOfLabels; i++) {
labels[i] = context.getLabelText(i);
}
forBlockState.labels = labels;
}
if (context.numberOfOperands != 2 && context.numberOfOperands != 3) {
context.addWrongNumberOfOperandsErrorMessage();
}
forBlockState.counter = null;
forBlockState.to = null;
if (context.numberOfOperands >= 1) {
forBlockState.counter = evaluateExpressionOperand(context, 0);
if (context.numberOfOperands >= 2) {
forBlockState.to = evaluateExpressionOperand(context, 1);
if (context.numberOfOperands >= 3) {
forBlockState.step = evaluateExpressionOperand(context, 2);
final Boolean stepIsNegative = Value.accept(
BinaryOperator.LESS_THAN.apply(forBlockState.step, ZERO, context.getEvaluationContext()),
ValueToBooleanVisitor.INSTANCE);
forBlockState.stepIsNegative = stepIsNegative != null && stepIsNegative.booleanValue();
} else {
forBlockState.step = ONE;
forBlockState.stepIsNegative = false;
}
}
}
forBlockState.parsed = true;
}
if (forBlockState.labels != null) {
if (forBlockState.labels instanceof String) {
defineVariable(context, (String) forBlockState.labels, forBlockState.counter);
} else {
final String[] labels = (String[]) forBlockState.labels;
for (String label : labels) {
defineVariable(context, label, forBlockState.counter);
}
}
}
final BinaryOperator operator = forBlockState.stepIsNegative ? BinaryOperator.GREATER_THAN_OR_EQUAL_TO
: BinaryOperator.LESS_THAN_OR_EQUAL_TO;
final Boolean result = Value.accept(
operator.apply(forBlockState.counter, forBlockState.to, context.getEvaluationContext()),
ValueToBooleanVisitor.INSTANCE);
if (!(result != null && result.booleanValue())) {
// Skip the block body and stop the iteration.
forBlockState.iterator.next();
forBlockState.hasNextIteration = false;
}
}
@Override
void defineLabels(M68KAssemblyContext context) {
// Don't define any labels.
}
}