package org.reasm.m68k.assembly.internal; import java.io.IOException; import javax.annotation.Nonnull; import org.reasm.Value; import org.reasm.m68k.messages.MinusOneDistanceShortBranchErrorMessage; import org.reasm.m68k.messages.ZeroDistanceShortBranchErrorMessage; /** * The <code>Bcc</code> instructions. * * @author Francis Gagné */ class BranchInstruction extends Instruction { @Nonnull static final BranchInstruction BCC = new BranchInstruction(IntegerConditionCode.CC); @Nonnull static final BranchInstruction BCS = new BranchInstruction(IntegerConditionCode.CS); @Nonnull static final BranchInstruction BEQ = new BranchInstruction(IntegerConditionCode.EQ); @Nonnull static final BranchInstruction BGE = new BranchInstruction(IntegerConditionCode.GE); @Nonnull static final BranchInstruction BGT = new BranchInstruction(IntegerConditionCode.GT); @Nonnull static final BranchInstruction BHI = new BranchInstruction(IntegerConditionCode.HI); @Nonnull static final BranchInstruction BHS = new BranchInstruction(IntegerConditionCode.HS); @Nonnull static final BranchInstruction BLE = new BranchInstruction(IntegerConditionCode.LE); @Nonnull static final BranchInstruction BLO = new BranchInstruction(IntegerConditionCode.LO); @Nonnull static final BranchInstruction BLS = new BranchInstruction(IntegerConditionCode.LS); @Nonnull static final BranchInstruction BLT = new BranchInstruction(IntegerConditionCode.LT); @Nonnull static final BranchInstruction BMI = new BranchInstruction(IntegerConditionCode.MI); @Nonnull static final BranchInstruction BNE = new BranchInstruction(IntegerConditionCode.NE); @Nonnull static final BranchInstruction BPL = new BranchInstruction(IntegerConditionCode.PL); @Nonnull static final BranchInstruction BRA = new BranchInstruction(IntegerConditionCode.T); @Nonnull static final BranchInstruction BSR = new BranchInstruction(IntegerConditionCode.F); @Nonnull static final BranchInstruction BVC = new BranchInstruction(IntegerConditionCode.VC); @Nonnull static final BranchInstruction BVS = new BranchInstruction(IntegerConditionCode.VS); @Nonnull private final IntegerConditionCode cc; private BranchInstruction(@Nonnull IntegerConditionCode cc) { this.cc = cc; } @Override void assemble2(final M68KAssemblyContext context) throws IOException { InstructionSize size = context.parseIntegerInstructionSize(); if (size == InstructionSize.INVALID) { if ("S".equalsIgnoreCase(context.attribute)) { size = InstructionSize.BYTE; } else { context.addInvalidSizeAttributeErrorMessage(); size = InstructionSize.DEFAULT; } } if (context.requireNumberOfOperands(1)) { final Value value = evaluateExpressionOperand(context, 0); final BranchLabelValueVisitor visitor = context.branchLabelValueVisitor; visitor.reset(size); short leadWord = (short) (0b01100000_00000000 | this.cc.ordinal() << 8); final InstructionSize outputSize; switch (size) { case DEFAULT: default: if (context.optimizeUnsizedBranches) { outputSize = InstructionSize.BYTE; } else { outputSize = InstructionSize.WORD; } break; case BYTE: outputSize = InstructionSize.BYTE; break; case WORD: outputSize = InstructionSize.WORD; break; case LONG: outputSize = InstructionSize.LONG; break; } visitor.outputSize = outputSize; // By default, generate a branch to self (BRA.B *-2). // For a byte-sized branch, putting 0 in the lower byte would turn the instruction into a word-sized branch (BRA.W). visitor.distance = -2; Value.accept(value, visitor); switch (visitor.outputSize) { case BYTE: short distance = (short) (visitor.distance & 0x00FF); if (distance == 0) { context.addTentativeMessage(new ZeroDistanceShortBranchErrorMessage()); distance = 0x00FE; } else if (distance == 0x00FF && InstructionSetCheck.MC68020_OR_LATER.isSupported(context.instructionSet)) { context.addTentativeMessage(new MinusOneDistanceShortBranchErrorMessage()); distance = 0x00FE; } leadWord |= distance; context.appendWord(leadWord); checkInstructionSet(InstructionSetCheck.M68000_FAMILY, context); break; case WORD: default: context.appendWord(leadWord); context.appendWord((short) visitor.distance); checkInstructionSet(InstructionSetCheck.M68000_FAMILY, context); break; case LONG: leadWord |= 0xFF; context.appendWord(leadWord); context.appendLong(visitor.distance); checkInstructionSet(InstructionSetCheck.MC68020_OR_LATER, context); break; } } } @Override void checkInstructionSet(M68KAssemblyContext context) { // Don't check here: depends on the effective instruction size. } }