package org.reasm.m68k.assembly.internal; import java.io.IOException; import java.util.EnumMap; import javax.annotation.Nonnull; import javax.annotation.concurrent.Immutable; import org.reasm.AssemblyMessage; import org.reasm.SymbolContext; import org.reasm.SymbolType; import org.reasm.Value; import org.reasm.m68k.messages.CountMustNotBeNegativeErrorMessage; import com.google.common.primitives.UnsignedLongs; /** * The <code>DS</code> and <code>RS</code> directives. * * @author Francis Gagné */ @Immutable abstract class DsRsDirective extends Mnemonic { @Nonnull static final DsRsDirective DS = new DsRsDirective() { @Override void assemble(M68KAssemblyContext context, long count, long itemSize) throws IOException { // If count * itemSize is greater than UnsignedLongs.MAX_VALUE, don't even try. if (UnsignedLongs.compare(count, UnsignedLongs.divide(UnsignedLongs.MAX_VALUE, itemSize)) > 0) { throw new OutOfMemoryError(); } final long byteCount = count * itemSize; for (long i = 0; i < byteCount; i++) { context.appendByte((byte) 0); } } @Override void automaticEven(M68KAssemblyContext context) throws IOException { context.automaticEven(); } }; @Nonnull static final DsRsDirective RS = new DsRsDirective() { @Override void assemble(M68KAssemblyContext context, long count, long itemSize) throws IOException { context.defineSymbols(SymbolContext.VALUE, SymbolType.CONSTANT, context.rs.getValue()); context.rs.incrementBy(count * itemSize); } @Override void automaticEven(M68KAssemblyContext context) { context.rs.automaticEven(); } @Override void defineLabels(M68KAssemblyContext context) { // Don't define any labels. } }; @Nonnull private static final CardinalValueVisitor.ErrorFactory NEGATIVE_VALUE_ERROR_FACTORY = new CardinalValueVisitor.ErrorFactory() { @Override public AssemblyMessage createMessage() { return new CountMustNotBeNegativeErrorMessage(); } }; @Nonnull private static final EnumMap<InstructionSize, Long> ITEM_SIZE_BY_INSTRUCTION_SIZE = new EnumMap<>(InstructionSize.class); static { ITEM_SIZE_BY_INSTRUCTION_SIZE.put(InstructionSize.DEFAULT, 2L); ITEM_SIZE_BY_INSTRUCTION_SIZE.put(InstructionSize.BYTE, 1L); ITEM_SIZE_BY_INSTRUCTION_SIZE.put(InstructionSize.WORD, 2L); ITEM_SIZE_BY_INSTRUCTION_SIZE.put(InstructionSize.LONG, 4L); ITEM_SIZE_BY_INSTRUCTION_SIZE.put(InstructionSize.QUAD, 8L); ITEM_SIZE_BY_INSTRUCTION_SIZE.put(InstructionSize.SINGLE, 4L); ITEM_SIZE_BY_INSTRUCTION_SIZE.put(InstructionSize.DOUBLE, 8L); ITEM_SIZE_BY_INSTRUCTION_SIZE.put(InstructionSize.EXTENDED, 12L); ITEM_SIZE_BY_INSTRUCTION_SIZE.put(InstructionSize.PACKED, 12L); ITEM_SIZE_BY_INSTRUCTION_SIZE.put(InstructionSize.INVALID, 2L); } @Override void assemble(M68KAssemblyContext context) throws IOException { InstructionSize size = context.parseInstructionSize(); if (size == InstructionSize.INVALID) { context.addInvalidSizeAttributeErrorMessage(); size = InstructionSize.DEFAULT; } if (size != InstructionSize.BYTE && context.automaticEven) { this.automaticEven(context); } if (context.requireNumberOfOperands(1)) { final Value countValue = evaluateExpressionOperand(context, 0); if (countValue != null) { final CardinalValueVisitor countVisitor = context.cardinalValueVisitor; countVisitor.reset(0, NEGATIVE_VALUE_ERROR_FACTORY); Value.accept(countValue, countVisitor); final long count = countVisitor.getValue(); final long itemSize = ITEM_SIZE_BY_INSTRUCTION_SIZE.get(size); this.assemble(context, count, itemSize); } } } abstract void assemble(@Nonnull M68KAssemblyContext context, long count, long itemSize) throws IOException; abstract void automaticEven(@Nonnull M68KAssemblyContext context) throws IOException; }