package org.reasm.m68k.assembly.internal; import java.io.IOException; import java.util.Collections; import java.util.EnumSet; import java.util.Set; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import javax.annotation.concurrent.Immutable; import org.reasm.Block; import org.reasm.BlockEvents; import org.reasm.Function; import org.reasm.Symbol; import org.reasm.Value; import org.reasm.ValueVisitor; import org.reasm.commons.source.LogicalLineReader; import org.reasm.expressions.Expression; import org.reasm.m68k.expressions.internal.ExpressionParser; import org.reasm.m68k.expressions.internal.InvalidTokenException; import org.reasm.m68k.expressions.internal.TokenType; import org.reasm.m68k.expressions.internal.Tokenizer; import org.reasm.m68k.messages.DuplicateRegistersInRegisterListWarningMessage; import org.reasm.m68k.messages.InvalidDataTypeForOrgOrObjDirectiveErrorMessage; import org.reasm.m68k.messages.InvalidExpressionErrorMessage; import org.reasm.m68k.messages.NotSupportedOnArchitectureErrorMessage; import org.reasm.m68k.source.M68KParser; /** * Base class for all instructions and directives provided by the M68000 architecture family. * * @author Francis Gagné */ @Immutable abstract class Mnemonic { /** * A value visitor for values that must be unsigned integers. Signed integers are accepted, but treated as unsigned integers. * * @author Francis Gagné */ @Immutable private static final class UnsignedIntValueVisitor implements ValueVisitor<Long> { public static final UnsignedIntValueVisitor INSTANCE = new UnsignedIntValueVisitor(); private UnsignedIntValueVisitor() { } @Override public Long visitFloat(double value) { return null; } @Override public Long visitFunction(Function value) { return null; } @Override public Long visitSignedInt(long value) { return value; } @Override public Long visitString(String value) { return null; } @Override public Long visitUndetermined() { return null; } @Override public Long visitUnsignedInt(long value) { return value; } } @Nonnull private static final GeneralPurposeRegister[] GENERAL_PURPOSE_REGISTERS = GeneralPurposeRegister.values(); static void checkInstructionSet(@Nonnull InstructionSetCheck instructionSetCheck, @Nonnull M68KAssemblyContext context) { if (!instructionSetCheck.isSupported(context.instructionSet)) { context.addMessage(new NotSupportedOnArchitectureErrorMessage()); } } static short encodeIntegerSizeStandard(@Nonnull InstructionSize size) { switch (size) { case BYTE: return 0b00 << 6; case WORD: case DEFAULT: default: return 0b01 << 6; case LONG: return 0b10 << 6; } } @CheckForNull static Value evaluateExpressionOperand(@Nonnull M68KAssemblyContext context, int operandIndex) { final Expression expression = parseExpressionOperand(context, operandIndex); if (expression != null) { return expression.evaluate(context.getEvaluationContext()); } return null; } @Nonnull static ScopedEffectBlockEvents getScopedEffectBlockEvents(@Nonnull M68KAssemblyContext context) { final Block block = context.builder.getCurrentBlock(); if (block == null) { throw new AssertionError(); } final BlockEvents blockEvents = block.getEvents(); if (blockEvents == null || !(blockEvents instanceof ScopedEffectBlockEvents)) { throw new AssertionError(); } return (ScopedEffectBlockEvents) blockEvents; } @CheckForNull static Expression parseExpressionOperand(@Nonnull M68KAssemblyContext context, int operandIndex) { final String operandText = context.getOperandText(operandIndex); final Tokenizer tokenizer = context.tokenizer; tokenizer.setCharSequence(operandText); try { final Expression expression = ExpressionParser.parse(tokenizer, context.createSymbolLookup(), context); if (expression != null && tokenizer.getTokenType() == TokenType.END) { return expression; } } catch (InvalidTokenException e) { } context.addMessage(new InvalidExpressionErrorMessage(operandText)); return null; } @CheckForNull static GeneralPurposeRegister parseRegister(@Nonnull M68KAssemblyContext context, @Nonnull LogicalLineReader operandReader) { final String registerIdentifier = parseRegisterIdentifier(operandReader); if (registerIdentifier != null) { return identifyRegister(context, registerIdentifier); } return null; } @CheckForNull static String parseRegisterIdentifier(@Nonnull LogicalLineReader operandReader) { if (!operandReader.atEnd() && M68KParser.SYNTAX.isValidIdentifierInitialCodePoint(operandReader.getCurrentCodePoint())) { final StringBuilder sb = new StringBuilder(); sb.appendCodePoint(operandReader.getCurrentCodePoint()); operandReader.advance(); while (!operandReader.atEnd() && operandReader.getCurrentCodePoint() != '.' && M68KParser.SYNTAX.isValidIdentifierCodePoint(operandReader.getCurrentCodePoint())) { sb.appendCodePoint(operandReader.getCurrentCodePoint()); operandReader.advance(); } return sb.toString(); } return null; } @CheckForNull static Set<GeneralPurposeRegister> parseRegisterList(@Nonnull M68KAssemblyContext context, int operandIndex) { context.prepareOperandReader(operandIndex); return parseRegisterList(context, context.logicalLineReader); } @CheckForNull static Set<GeneralPurposeRegister> parseRegisterList(M68KAssemblyContext context, LogicalLineReader operandReader) { final int initialPosition = operandReader.backupPosition(); try { boolean firstIteration = true; final EnumSet<GeneralPurposeRegister> registers = EnumSet.noneOf(GeneralPurposeRegister.class); final EnumSet<GeneralPurposeRegister> duplicateRegisters = EnumSet.noneOf(GeneralPurposeRegister.class); for (;;) { // Parse a single register (Rx) or a register range (Rx-Ry). final String registerIdentifier = parseRegisterIdentifier(operandReader); if (registerIdentifier == null) { return null; } GeneralPurposeRegister firstRegisterOfRange = GeneralPurposeRegister.identify(registerIdentifier); if (firstRegisterOfRange == null) { if (firstIteration && operandReader.atEnd()) { // Check if the identifier is a register list alias. final Symbol symbol = context.getRegisterAliasOrRegisterListAliasSymbolByName(registerIdentifier); if (symbol != null) { final Object value = symbol.getValue(); if (value instanceof RegisterList) { return ((RegisterList) value).getRegisters(); } if (value instanceof GeneralPurposeRegister) { firstRegisterOfRange = (GeneralPurposeRegister) value; } } } else { firstRegisterOfRange = context.getRegisterAliasByName(registerIdentifier); } } if (firstRegisterOfRange == null) { return null; } operandReader.skipWhitespace(); if (!operandReader.atEnd() && operandReader.getCurrentCodePoint() == '-') { operandReader.advance(); operandReader.skipWhitespace(); GeneralPurposeRegister secondRegisterOfRange = parseRegister(context, operandReader); if (secondRegisterOfRange == null) { return null; } if (secondRegisterOfRange.compareTo(firstRegisterOfRange) < 0) { // Swap the registers. GeneralPurposeRegister temp = secondRegisterOfRange; secondRegisterOfRange = firstRegisterOfRange; firstRegisterOfRange = temp; } for (int i = firstRegisterOfRange.ordinal(); i <= secondRegisterOfRange.ordinal(); i++) { addRegister(GENERAL_PURPOSE_REGISTERS[i], registers, duplicateRegisters); } } else { addRegister(firstRegisterOfRange, registers, duplicateRegisters); } operandReader.skipWhitespace(); // If the operand ends here, we have successfully parsed a register list. if (operandReader.atEnd()) { break; } // A slash separates register ranges. if (operandReader.getCurrentCodePoint() != '/') { return null; } firstIteration = false; operandReader.advance(); operandReader.skipWhitespace(); } if (!duplicateRegisters.isEmpty()) { context.addTentativeMessage(new DuplicateRegistersInRegisterListWarningMessage()); } return Collections.unmodifiableSet(registers); } finally { operandReader.restorePosition(initialPosition); } } static boolean parseSpecialRegister(@Nonnull M68KAssemblyContext context, int operandIndex, @Nonnull String registerName) { final LogicalLineReader reader = context.logicalLineReader; context.prepareOperandReader(operandIndex); int expectedCodePoint; for (int i = 0; i < registerName.length(); i += Character.charCount(expectedCodePoint), reader.advance()) { if (reader.atEnd()) { return false; } final int actualCodePoint = reader.getCurrentCodePoint(); expectedCodePoint = registerName.codePointAt(i); assert expectedCodePoint >= 'A' && expectedCodePoint <= 'Z'; if (actualCodePoint != expectedCodePoint && actualCodePoint != (expectedCodePoint | 0x20)) { return false; } } return reader.atEnd(); } @CheckForNull static Long readSingleUnsignedIntOperand(@Nonnull M68KAssemblyContext context) { if (context.requireNumberOfOperands(1)) { final Value value = evaluateExpressionOperand(context, 0); if (value != null) { final Long longValue = Value.accept(value, UnsignedIntValueVisitor.INSTANCE); if (longValue == null) { context.addTentativeMessage(new InvalidDataTypeForOrgOrObjDirectiveErrorMessage()); } return longValue; } } return null; } private static void addRegister(@Nonnull GeneralPurposeRegister register, @Nonnull EnumSet<GeneralPurposeRegister> registers, @Nonnull EnumSet<GeneralPurposeRegister> duplicateRegisters) { if (!registers.add(register)) { duplicateRegisters.add(register); } } @CheckForNull private static GeneralPurposeRegister identifyRegister(@Nonnull M68KAssemblyContext context, @Nonnull String registerIdentifier) { final GeneralPurposeRegister reg = GeneralPurposeRegister.identify(registerIdentifier); if (reg != null) { return reg; } return context.getRegisterAliasByName(registerIdentifier); } /** * Assembles the directive or instruction on the logical line of the context's current assembly step. * * @param context * the assembly context * @throws IOException * an I/O exception occurred */ abstract void assemble(@Nonnull M68KAssemblyContext context) throws IOException; void checkInstructionSet(@Nonnull M68KAssemblyContext context) { checkInstructionSet(InstructionSetCheck.M68000_FAMILY, context); } void defineLabels(@Nonnull M68KAssemblyContext context) { context.defineLabels(); } }