package org.reasm.m68k.assembly.internal; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.Set; import java.util.Stack; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import javax.annotation.concurrent.Immutable; import org.reasm.AssemblyMessage; import org.reasm.Function; import org.reasm.Value; import org.reasm.ValueVisitor; import org.reasm.commons.messages.AddressingModeNotAllowedHereErrorMessage; import org.reasm.commons.messages.FunctionCannotBeConvertedToIntegerErrorMessage; import org.reasm.commons.messages.LossyConversionFromRealToIntegerWarningMessage; import org.reasm.commons.messages.StringTooLongErrorMessage; import org.reasm.commons.messages.SyntaxErrorInEffectiveAddressErrorMessage; import org.reasm.commons.messages.ValueOutOfRangeErrorMessage; import org.reasm.expressions.*; 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.AddressingModeNotSupportedErrorMessage; import org.reasm.m68k.messages.BaseDisplacementOutOfRangeErrorMessage; import org.reasm.m68k.messages.EndOfExpressionExpectedErrorMessage; import org.reasm.m68k.messages.ExpressionExpectedErrorMessage; import org.reasm.m68k.messages.InvalidScaleValueErrorMessage; import org.reasm.m68k.messages.ScaleSpecificationNotSupportedErrorMessage; import ca.fragag.Consumer; /** * Provides methods and constants to work with effective addresses. * * @author Francis Gagné */ final class EffectiveAddress { static final class IntegerValueVisitor implements ValueVisitor<Integer> { @Nonnull private final InstructionSize instructionSize; @Nonnull private final Charset encoding; @Nonnull private final Consumer<AssemblyMessage> assemblyMessageConsumer; IntegerValueVisitor(@Nonnull InstructionSize instructionSize, @Nonnull Charset encoding, @Nonnull Consumer<AssemblyMessage> assemblyMessageConsumer) { this.instructionSize = instructionSize; this.encoding = encoding; this.assemblyMessageConsumer = assemblyMessageConsumer; } @Override public Integer visitFloat(double value) { if (value != (int) value) { this.assemblyMessageConsumer.accept(new LossyConversionFromRealToIntegerWarningMessage(value)); } return this.visitUnsignedInt((int) value); } @Override public Integer visitFunction(Function value) { this.assemblyMessageConsumer.accept(new FunctionCannotBeConvertedToIntegerErrorMessage()); return null; } @Override public Integer visitSignedInt(long value) { return this.visitUnsignedInt(value); } @Override public Integer visitString(String value) { int maxLength; switch (this.instructionSize) { case BYTE: maxLength = 1; break; default: maxLength = 2; break; case LONG: maxLength = 4; break; } final ByteBuffer stringBytes = this.encoding.encode(value); if (stringBytes.limit() > maxLength) { this.assemblyMessageConsumer.accept(new StringTooLongErrorMessage(value)); } if (maxLength > stringBytes.limit()) { maxLength = stringBytes.limit(); } int result = 0; for (; maxLength != 0; maxLength--) { result <<= 8; result |= stringBytes.get() & 0xFF; } return result; } @Override public Integer visitUndetermined() { return null; } @Override public Integer visitUnsignedInt(long value) { int intValue = (int) value; switch (this.instructionSize) { case BYTE: if (intValue < -0x80 || intValue > 0xFF) { this.assemblyMessageConsumer.accept(new ValueOutOfRangeErrorMessage(value)); } return (int) (short) (intValue & 0xFF); default: if (intValue < -0x8000 || intValue > 0xFFFF) { this.assemblyMessageConsumer.accept(new ValueOutOfRangeErrorMessage(value)); } return (int) (short) intValue; case LONG: return intValue; } } } @Immutable enum MemoryIndirectMode { NONE, PREINDEXED, POSTINDEXED } @Immutable enum MemoryIndirectState { NONE, INCOMPLETE, COMPLETE } static final class QuadParser { @CheckForNull Expression baseDisplacementExpression; boolean haveBaseRegister; int baseRegister; @CheckForNull Expression baseRegisterExpression; boolean haveIndexRegister; int indexRegister; boolean preindexed; @CheckForNull Expression outerDisplacementExpression; @Nonnull MemoryIndirectState memoryIndirectState = MemoryIndirectState.NONE; QuadParser() { } void encode(@Nonnull Set<AddressingMode> validAddressingModes, int offsetToExtensionWords, @Nonnull EvaluationContext evaluationContext, @Nonnull M68KBasicAssemblyContext context, @Nonnull Consumer<AssemblyMessage> assemblyMessageConsumer, @Nonnull EffectiveAddress result) { final MemoryIndirectMode memoryIndirectMode; switch (this.memoryIndirectState) { case NONE: memoryIndirectMode = MemoryIndirectMode.NONE; break; case COMPLETE: if (!this.haveIndexRegister || this.preindexed) { memoryIndirectMode = MemoryIndirectMode.PREINDEXED; } else { memoryIndirectMode = MemoryIndirectMode.POSTINDEXED; } break; default: throw new AssertionError(); } final boolean haveBaseDisplacement = this.baseDisplacementExpression != null; int baseDisplacement; if (this.haveBaseRegister && (this.baseRegister == 8 || this.baseRegister == 9)) { baseDisplacement = (int) context.programCounter; } else { baseDisplacement = 0; } if (haveBaseDisplacement) { final Integer value = Value.accept(this.baseDisplacementExpression.evaluate(evaluationContext), new IntegerValueVisitor(InstructionSize.LONG, context.encoding, assemblyMessageConsumer)); if (value != null) { baseDisplacement = value; } } final boolean haveOuterDisplacement = this.outerDisplacementExpression != null; int outerDisplacement = 0; if (haveOuterDisplacement) { final Integer value = Value.accept(this.outerDisplacementExpression.evaluate(evaluationContext), new IntegerValueVisitor(InstructionSize.LONG, context.encoding, assemblyMessageConsumer)); if (value != null) { outerDisplacement = value; } } encodeQuad(haveBaseDisplacement, baseDisplacement, this.haveBaseRegister, this.baseRegister, this.haveIndexRegister, this.indexRegister, outerDisplacement, memoryIndirectMode, validAddressingModes, offsetToExtensionWords, context, assemblyMessageConsumer, result); } boolean processExpression(@Nonnull Expression expression, @Nonnull EvaluationContext evaluationContext, @Nonnull M68KBasicAssemblyContext context, @Nonnull Consumer<AssemblyMessage> assemblyMessageConsumer) { if (!this.haveBaseRegister || !this.haveIndexRegister) { if (expression instanceof IdentifierExpression) { final String identifier = ((IdentifierExpression) expression).getIdentifier(); if (!this.haveBaseRegister || this.baseRegister < 8) { final short reg = identifyPcOrZpcRegister(identifier); if (reg != -1) { // If we have a base register, move it to the index register. if (this.haveBaseRegister) { this.haveIndexRegister = true; this.indexRegister = 0b10000000 | this.baseRegister << 4; } this.haveBaseRegister = true; this.baseRegister = reg + 8; this.baseRegisterExpression = expression; return true; } } final short reg = identifyDataOrAddressRegister(identifier, false, context); if (reg != -1) { if ((reg & MODE_MASK) == MODE_ADDRESS_REGISTER_DIRECT && !this.haveBaseRegister) { this.haveBaseRegister = true; this.baseRegister = reg & REGISTER_MASK; this.baseRegisterExpression = expression; return true; } if (!this.haveIndexRegister) { this.haveIndexRegister = true; this.indexRegister = reg << 4; if (this.memoryIndirectState == MemoryIndirectState.INCOMPLETE) { this.preindexed = true; } return true; } } } } if (!this.haveIndexRegister) { int indexReg = parseIndexRegister(expression, evaluationContext, context, assemblyMessageConsumer); if (indexReg != -1) { this.haveIndexRegister = true; this.indexRegister = indexReg; if (this.memoryIndirectState == MemoryIndirectState.INCOMPLETE) { this.preindexed = true; } return true; } } if (this.baseDisplacementExpression == null && this.memoryIndirectState != MemoryIndirectState.COMPLETE || this.outerDisplacementExpression == null && this.memoryIndirectState == MemoryIndirectState.COMPLETE) { if (this.baseDisplacementExpression == null && this.memoryIndirectState != MemoryIndirectState.COMPLETE) { this.baseDisplacementExpression = expression; } else { this.outerDisplacementExpression = expression; } return true; } return false; } } @Immutable private enum AbsoluteAddressingSize { DEFAULT, WORD, LONG } /** The mask for the register in an effective address. */ static final int REGISTER_MASK = 0b000111; /** The mask for the mode in an effective address. */ static final int MODE_MASK = 0b111000; /** The mask for the effective address. */ static final int EA_MASK = MODE_MASK | REGISTER_MASK; /** The data register direct mode (e.g. d0). */ static final int MODE_DATA_REGISTER_DIRECT = 0b000000; /** The address register direct mode (e.g. a0). */ static final int MODE_ADDRESS_REGISTER_DIRECT = 0b001000; /** The address register indirect mode (e.g. (a0)). */ static final int MODE_ADDRESS_REGISTER_INDIRECT = 0b010000; /** The address register indirect with postincrement mode (e.g. (a0)+). */ static final int MODE_ADDRESS_REGISTER_INDIRECT_WITH_POSTINCREMENT = 0b011000; /** The address register indirect with predecrement mode (e.g. -(a0)). */ static final int MODE_ADDRESS_REGISTER_INDIRECT_WITH_PREDECREMENT = 0b100000; /** The address register indirect with displacement mode (e.g. 2(a0), (2,a0)). */ static final int MODE_ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT = 0b101000; /** The address register indirect indexed mode (e.g. (a0,d0)). */ static final int MODE_ADDRESS_REGISTER_INDIRECT_INDEXED = 0b110000; /** The mode value for other modes (in these modes, the register part of the effective address is not actually a register). */ static final int MODE_OTHERS = 0b111000; /** The absolute short addressing mode (e.g. ($1000).w). */ static final int EA_ABSOLUTE_SHORT_ADDRESSING = 0b111000; /** The absolute long addressing mode (e.g. ($100000).l). */ static final int EA_ABSOLUTE_LONG_ADDRESSING = 0b111001; /** The program counter indirect with displacement mode (e.g. -4(pc), (-4,pc)). */ static final int EA_PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT = 0b111010; /** The program counter indirect indexed mode (e.g. (pc,d0), -4(pc,d0), (-4,pc,d0)). */ static final int EA_PROGRAM_COUNTER_INDIRECT_INDEXED = 0b111011; /** The immediate data mode (e.g. #$ABCD). */ static final int EA_IMMEDIATE_DATA = 0b111100; // Single effective address operation word format // ---------------------------------------------- // // Bits 5-3: mode // Bits 2-0: register // Brief extension word format // --------------------------- // // Bit 15: index register type (D/A) // 0: data register // 1: address register // Bits 14-12: index register // Bit 11: word/long-word index size // 0: word size // 1: long size // Bits 10-9: scale // 0: Xn*1 // 1: Xn*2 // 2: Xn*4 // 3: Xn*8 // Bit 8: 0 // Bits 7-0: displacement // Full extension word format // -------------------------- // // Bit 15: index register type (D/A) // 0: data register // 1: address register // Bits 14-12: index register // Bit 11: word/long-word index size // 0: word size // 1: long size // Bits 10-9: scale // 0: Xn*1 // 1: Xn*2 // 2: Xn*4 // 3: Xn*8 // Bit 8: 1 // Bit 7: base register suppress // 0: base register is present // 1: base register is suppressed // Bit 6: index suppress // 0: index register is present // 1: index register is suppressed // Bits 5-4: base displacement size // 00: reserved // 01: null base displacement // 10: word-sized base displacement // 11: long-sized base displacement // Bit 3: 0 // Bits 2-0: index/indirect selection // if index register is present: // 000: no memory indirect action // 001: memory indirect preindexed with null outer displacement // 010: memory indirect preindexed with word-sized outer displacement // 011: memory indirect preindexed with long-sized outer displacement // 100: reserved // 101: memory indirect postindexed with null outer displacement // 110: memory indirect postindexed with word-sized outer displacement // 111: memory indirect postindexed with long-sized outer displacement // if index register is suppressed: // 000: no memory indirect action // 001: memory indirect with null outer displacement // 010: memory indirect with word-sized outer displacement // 011: memory indirect with long-sized outer displacement // 100-111: reserved static void encodeQuad(boolean haveBaseDisplacement, int baseDisplacement, boolean haveBaseRegister, int baseRegister, boolean haveIndexRegister, int indexRegister, int outerDisplacement, @Nonnull MemoryIndirectMode memoryIndirectMode, @Nonnull Set<AddressingMode> validAddressingModes, int offsetToExtensionWords, @Nonnull M68KBasicAssemblyContext context, @Nonnull Consumer<AssemblyMessage> assemblyMessageConsumer, @Nonnull EffectiveAddress result) { // If we have a base register, but it's ZPC, clear the haveBaseRegister flag. if (haveBaseRegister && baseRegister == 9) { haveBaseRegister = false; } // If the base register is PC or ZPC, the base displacement is interpreted as an absolute address, // while the encoded value is relative to the address of the (first) extension word. if (baseRegister == 8 || baseRegister == 9) { haveBaseDisplacement = true; baseDisplacement -= (int) context.programCounter + offsetToExtensionWords; } final boolean haveAddressRegister = haveBaseRegister && baseRegister < 8; final int baseDisplacementSize = calculateDisplacementSize(baseDisplacement); final int outerDisplacementSize = calculateDisplacementSize(outerDisplacement); // If the instruction set doesn't support a large base displacement, // and an addressing mode with a shorter encoding would be valid if the base displacement wasn't out of range, // we use that addressing mode to keep the size of the instruction more stable, // which helps reduce the number of passes required to assemble the source. // We also produce a more specific error (BaseDisplacementOutOfRangeErrorMessage) // if the base displacement is indeed out of range. final boolean supportsFullExtensionWordFormat = context.instructionSet.supportsFullExtensionWordFormat(); if (memoryIndirectMode == MemoryIndirectMode.NONE && haveBaseRegister) { // Look for shorter encodings. if (haveBaseDisplacement && !haveIndexRegister && (!supportsFullExtensionWordFormat || baseDisplacementSize <= 1)) { if (haveAddressRegister) { boolean optimizeZeroDisplacement = context.optimizeZeroDisplacement; if (optimizeZeroDisplacement && baseDisplacement == 0) { // (0,An) --> (An) encodeAddressRegisterIndirect(baseRegister, validAddressingModes, assemblyMessageConsumer, result); return; } // (d16,An) result.word0 = (short) (MODE_ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT | baseRegister); validateAddressingMode(validAddressingModes, AddressingMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT, assemblyMessageConsumer); } else { // (d16,PC) result.word0 = EA_PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT; validateAddressingMode(validAddressingModes, AddressingMode.PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT, assemblyMessageConsumer); } result.numberOfWords = 2; result.word1 = (short) baseDisplacement; if (!supportsFullExtensionWordFormat && baseDisplacementSize > 1) { assemblyMessageConsumer.accept(new BaseDisplacementOutOfRangeErrorMessage()); } return; } // Do not check for the presence of the base displacement; use 0 if it was not specified explicitly. if (haveIndexRegister && (!supportsFullExtensionWordFormat || fitsInByte(baseDisplacement))) { if (haveAddressRegister) { // (d8,An,Xn) result.word0 = (short) (MODE_ADDRESS_REGISTER_INDIRECT_INDEXED | baseRegister); validateAddressingMode(validAddressingModes, AddressingMode.ADDRESS_REGISTER_INDIRECT_INDEXED, assemblyMessageConsumer); } else { // (d8,PC,Xn) result.word0 = EA_PROGRAM_COUNTER_INDIRECT_INDEXED; validateAddressingMode(validAddressingModes, AddressingMode.PROGRAM_COUNTER_INDIRECT_INDEXED, assemblyMessageConsumer); } result.numberOfWords = 2; result.word1 = (short) (indexRegister << 8 | baseDisplacement & 0xFF); // If indexRegister encodes a scale, validate that the target architecture supports it. if ((indexRegister & 0b00000110) != 0 && !context.instructionSet.supportsScale()) { assemblyMessageConsumer.accept(new ScaleSpecificationNotSupportedErrorMessage()); } if (!supportsFullExtensionWordFormat && !fitsInByte(baseDisplacement)) { assemblyMessageConsumer.accept(new BaseDisplacementOutOfRangeErrorMessage()); } return; } } result.numberOfWords = 2; if (baseRegister < 8) { result.word0 = (short) (MODE_ADDRESS_REGISTER_INDIRECT_INDEXED | baseRegister); validateAddressingMode(validAddressingModes, AddressingMode.ADDRESS_REGISTER_INDIRECT_INDEXED, assemblyMessageConsumer); } else { result.word0 = EA_PROGRAM_COUNTER_INDIRECT_INDEXED; validateAddressingMode(validAddressingModes, AddressingMode.PROGRAM_COUNTER_INDIRECT_INDEXED, assemblyMessageConsumer); } result.word1 = (short) (indexRegister << 8 | 0b00000001_00000000 | baseDisplacementSize + 1 << 4); if (!haveBaseRegister) { result.word1 |= 0b00000000_10000000; } if (!haveIndexRegister) { result.word1 |= 0b00000000_01000000; } if (memoryIndirectMode == MemoryIndirectMode.POSTINDEXED) { result.word1 |= 0b00000000_00000100; } if (memoryIndirectMode != MemoryIndirectMode.NONE) { result.word1 |= outerDisplacementSize + 1; } if (baseDisplacementSize == 1) { result.setWord(result.numberOfWords++, (short) baseDisplacement); } else if (baseDisplacementSize == 2) { result.setWord(result.numberOfWords++, (short) (baseDisplacement >> 16)); result.setWord(result.numberOfWords++, (short) baseDisplacement); } if (outerDisplacementSize == 1) { result.setWord(result.numberOfWords++, (short) outerDisplacement); } else if (outerDisplacementSize == 2) { result.setWord(result.numberOfWords++, (short) (outerDisplacement >> 16)); result.setWord(result.numberOfWords++, (short) outerDisplacement); } if (memoryIndirectMode == MemoryIndirectMode.NONE) { if (!supportsFullExtensionWordFormat) { assemblyMessageConsumer.accept(new AddressingModeNotSupportedErrorMessage()); } } else { if (!context.instructionSet.supportsMemoryIndirect()) { assemblyMessageConsumer.accept(new AddressingModeNotSupportedErrorMessage()); } } } static void getEffectiveAddress(@Nonnull Tokenizer tokenizer, @CheckForNull SymbolLookup symbolLookup, @Nonnull Set<AddressingMode> validAddressingModes, boolean expectBitFieldSpecificationAtEnd, @Nonnull InstructionSize instructionSize, int offsetToExtensionWords, @Nonnull EvaluationContext evaluationContext, @Nonnull M68KBasicAssemblyContext context, @Nonnull Consumer<AssemblyMessage> assemblyMessageConsumer, @Nonnull EffectiveAddress result) { // Clear the result. result.numberOfWords = 0; result.word0 = 0; result.word1 = 0; result.word2 = 0; result.word3 = 0; result.word4 = 0; result.word5 = 0; switch (instructionSize) { case BYTE: case WORD: case LONG: break; case DEFAULT: instructionSize = InstructionSize.WORD; break; default: throw new AssertionError("Unexpected instruction size: " + instructionSize); } try { if (tokenizer.getTokenType() == TokenType.IMMEDIATE) { switch (instructionSize) { default: result.numberOfWords = 2; break; case LONG: result.numberOfWords = 3; break; } result.word0 = EA_IMMEDIATE_DATA; tokenizer.advance(); final Expression expression = ExpressionParser.parse(tokenizer, symbolLookup, assemblyMessageConsumer); if (expression == null) { assemblyMessageConsumer.accept(new ExpressionExpectedErrorMessage()); return; } final Value value = expression.evaluate(evaluationContext); final Integer intValue = Value.accept(value, new IntegerValueVisitor(instructionSize, context.encoding, assemblyMessageConsumer)); if (intValue != null) { switch (instructionSize) { default: result.word1 = intValue.shortValue(); break; case LONG: result.word1 = (short) (intValue >> 16); result.word2 = intValue.shortValue(); break; } } if (!endOfEA(tokenizer, expectBitFieldSpecificationAtEnd)) { assemblyMessageConsumer.accept(new EndOfExpressionExpectedErrorMessage()); } validateAddressingMode(validAddressingModes, AddressingMode.IMMEDIATE_DATA, assemblyMessageConsumer); } else { final Expression expression = ExpressionParser.parse(tokenizer, symbolLookup, assemblyMessageConsumer); if (expression == null) { if (tokenizer.getTokenType() == TokenType.OPENING_PARENTHESIS) { parseQuad(tokenizer, symbolLookup, expectBitFieldSpecificationAtEnd, null, validAddressingModes, offsetToExtensionWords, evaluationContext, context, assemblyMessageConsumer, result); } } else { // If the token following the expression is a (, parse a quad using the expression as the base displacement. // Otherwise, analyze the expression and turn it into an effective address. if (tokenizer.getTokenType() == TokenType.OPENING_PARENTHESIS) { parseQuad(tokenizer, symbolLookup, expectBitFieldSpecificationAtEnd, expression, validAddressingModes, offsetToExtensionWords, evaluationContext, context, assemblyMessageConsumer, result); } else if (tokenizer.tokenEqualsString("+")) { if (expression instanceof GroupingExpression) { int addressRegister = parseAddressRegisterIndirect((GroupingExpression) expression, context); if (addressRegister != -1) { tokenizer.advance(); if (endOfEA(tokenizer, expectBitFieldSpecificationAtEnd)) { result.numberOfWords = 1; result.word0 = (short) (MODE_ADDRESS_REGISTER_INDIRECT_WITH_POSTINCREMENT | addressRegister); validateAddressingMode(validAddressingModes, AddressingMode.ADDRESS_REGISTER_INDIRECT_WITH_POSTINCREMENT, assemblyMessageConsumer); } } } } else if (endOfEA(tokenizer, expectBitFieldSpecificationAtEnd)) { // Analyze the expression to see if it matches an effective address. if (expression instanceof IdentifierExpression) { // Check if the identifier matches a register name. final IdentifierExpression identifierExpression = (IdentifierExpression) expression; final String identifier = identifierExpression.getIdentifier(); final short reg = identifyDataOrAddressRegister(identifier, false, context); if (reg != -1) { result.numberOfWords = 1; result.word0 = reg; validateAddressingMode(validAddressingModes, (reg & MODE_MASK) == MODE_DATA_REGISTER_DIRECT ? AddressingMode.DATA_REGISTER_DIRECT : AddressingMode.ADDRESS_REGISTER_DIRECT, assemblyMessageConsumer); } final int absoluteAddressingSize = parseIndexRegisterOrAbsoluteAddressingSize(identifier); if (absoluteAddressingSize != -1) { encodeAbsoluteAddressing(new IdentifierExpression(identifier.substring(0, identifier.length() - 2), identifierExpression.getSymbolLookup()), absoluteAddressingSize == 0 ? AbsoluteAddressingSize.WORD : AbsoluteAddressingSize.LONG, validAddressingModes, evaluationContext, assemblyMessageConsumer, result, offsetToExtensionWords, context); } } else if (expression instanceof GroupingExpression) { final Expression childExpression = ((GroupingExpression) expression).getChildExpression(); if (childExpression instanceof IdentifierExpression) { // Check if the identifier matches a register name. final String identifier = ((IdentifierExpression) childExpression).getIdentifier(); short reg = identifyPcOrZpcRegister(identifier); if (reg != -1) { encodeQuad(false, 0, true, reg + 8, false, 0, 0, MemoryIndirectMode.NONE, validAddressingModes, offsetToExtensionWords, context, assemblyMessageConsumer, result); } else { reg = identifyDataOrAddressRegister(identifier, false, context); if (reg != -1 && (reg & MODE_MASK) == MODE_ADDRESS_REGISTER_DIRECT) { encodeAddressRegisterIndirect(reg & REGISTER_MASK, validAddressingModes, assemblyMessageConsumer, result); } } } if (result.numberOfWords == 0) { int indexReg = parseIndexRegister(((GroupingExpression) expression).getChildExpression(), evaluationContext, context, assemblyMessageConsumer); if (indexReg != -1) { encodeQuad(false, 0, false, 0, true, indexReg, 0, MemoryIndirectMode.NONE, validAddressingModes, offsetToExtensionWords, context, assemblyMessageConsumer, result); } } } else if (expression instanceof UnaryOperatorExpression) { final Expression operand = ((UnaryOperatorExpression) expression).getOperand(); if (((UnaryOperatorExpression) expression).getOperator() == UnaryOperator.NEGATION && operand instanceof GroupingExpression) { int addressRegister = parseAddressRegisterIndirect((GroupingExpression) operand, context); if (addressRegister != -1) { result.numberOfWords = 1; result.word0 = (short) (MODE_ADDRESS_REGISTER_INDIRECT_WITH_PREDECREMENT | addressRegister); validateAddressingMode(validAddressingModes, AddressingMode.ADDRESS_REGISTER_INDIRECT_WITH_PREDECREMENT, assemblyMessageConsumer); } } } if (result.numberOfWords == 0) { // MRI syntax is problematic because when we parse it as an expression, precedence rules can make the // parentheses around the base and index registers hidden deep in the expression tree. // For example, in: // 2+4(A0) // the expression tree looks like this: // BinaryOperatorExpression // operator=ADDITION // left=ValueExpression (value=2) // right=FunctionCallExpression // function=ValueExpression (value=4) // arguments[0]=IdentifierExpression (identifier=A0) // However, what we want is an expression for 2+4: // BinaryOperatorExpression // operator=ADDITION // left=ValueExpression (value=2) // right=ValueExpression (value=4) // To get this, we need to "deconstruct" the expression and construct a new one. // But first, we need to find the FunctionCallExpression. // It can be the operand of an UnaryOperatorExpression, the right operand of a BinaryOperatorExpression // or the false part of a ConditionalExpression, recursively. // // The same process also applies for absolute addressing when the parentheses are omitted (e.g. 2+2.W). Stack<Expression> expressionStack = null; Expression currentExpression = expression; for (;;) { final Expression newExpression; if (currentExpression instanceof FunctionCallExpression) { final Expression[] arguments = ((FunctionCallExpression) currentExpression).getArguments(); if (arguments.length > 0) { boolean isValid = true; QuadParser quad = new QuadParser(); quad.baseDisplacementExpression = reconstructExpressionWithNewTail(expressionStack, ((FunctionCallExpression) currentExpression).getFunction()); for (Expression argument : arguments) { if (!quad.processExpression(argument, evaluationContext, context, assemblyMessageConsumer)) { isValid = false; break; } } if (isValid) { quad.encode(validAddressingModes, offsetToExtensionWords, evaluationContext, context, assemblyMessageConsumer, result); } } break; } else if (currentExpression instanceof PeriodExpression) { final Expression leftExpression = ((PeriodExpression) currentExpression).getLeftExpression(); final Expression rightExpression = ((PeriodExpression) currentExpression).getRightExpression(); if (rightExpression instanceof IdentifierExpression) { final String identifier = ((IdentifierExpression) rightExpression).getIdentifier(); if (identifier.length() == 1) { if (equalsAsciiCaseInsensitive(identifier.charAt(0), 'W')) { encodeAbsoluteAddressing( reconstructExpressionWithNewTail(expressionStack, leftExpression), AbsoluteAddressingSize.WORD, validAddressingModes, evaluationContext, assemblyMessageConsumer, result, offsetToExtensionWords, context); } else if (equalsAsciiCaseInsensitive(identifier.charAt(0), 'L')) { encodeAbsoluteAddressing( reconstructExpressionWithNewTail(expressionStack, leftExpression), AbsoluteAddressingSize.LONG, validAddressingModes, evaluationContext, assemblyMessageConsumer, result, offsetToExtensionWords, context); } } } break; } else if (currentExpression instanceof UnaryOperatorExpression) { if (expressionStack == null) { expressionStack = new Stack<>(); } expressionStack.push(currentExpression); newExpression = ((UnaryOperatorExpression) currentExpression).getOperand(); } else if (currentExpression instanceof BinaryOperatorExpression) { if (expressionStack == null) { expressionStack = new Stack<>(); } expressionStack.push(currentExpression); newExpression = ((BinaryOperatorExpression) currentExpression).getOperand2(); } else if (currentExpression instanceof ConditionalExpression) { if (expressionStack == null) { expressionStack = new Stack<>(); } expressionStack.push(currentExpression); newExpression = ((ConditionalExpression) currentExpression).getFalsePart(); } else { break; } currentExpression = newExpression; } } if (result.numberOfWords == 0) { encodeAbsoluteAddressing(expression, AbsoluteAddressingSize.DEFAULT, validAddressingModes, evaluationContext, assemblyMessageConsumer, result, offsetToExtensionWords, context); } } } if (result.numberOfWords == 0) { assemblyMessageConsumer.accept(new SyntaxErrorInEffectiveAddressErrorMessage()); } } } catch (InvalidTokenException e) { assemblyMessageConsumer.accept(e.createAssemblyErrorMessage()); } } static short identifyDataOrAddressRegister(@Nonnull String identifier, boolean mayHaveSizeSuffix, @Nonnull M68KBasicAssemblyContext context) { { final GeneralPurposeRegister reg = GeneralPurposeRegister.identify(identifier); if (reg != null) { return (short) reg.ordinal(); } } if (mayHaveSizeSuffix || parseIndexRegisterOrAbsoluteAddressingSize(identifier) == -1) { final GeneralPurposeRegister reg = context.getRegisterAliasByName(identifier); if (reg != null) { return (short) reg.ordinal(); } } return -1; } static short identifyPcOrZpcRegister(@Nonnull String identifier) { if (identifier.length() == 2) { if (equalsAsciiCaseInsensitive(identifier.charAt(0), 'P') && equalsAsciiCaseInsensitive(identifier.charAt(1), 'C')) { return 0; } } else if (identifier.length() == 3) { if (equalsAsciiCaseInsensitive(identifier.charAt(0), 'Z') && equalsAsciiCaseInsensitive(identifier.charAt(1), 'P') && equalsAsciiCaseInsensitive(identifier.charAt(2), 'C')) { return 1; } } return -1; } static int parseIndexRegister(@Nonnull Expression expression, @Nonnull EvaluationContext evaluationContext, @Nonnull M68KBasicAssemblyContext context, @Nonnull Consumer<AssemblyMessage> assemblyMessageConsumer) { if (expression instanceof BinaryOperatorExpression) { final BinaryOperatorExpression binaryOperatorExpression = (BinaryOperatorExpression) expression; if (binaryOperatorExpression.getOperator() == BinaryOperator.MULTIPLICATION) { final int indexReg = parseIndexRegisterName(binaryOperatorExpression.getOperand1(), context); if (indexReg != -1) { final Integer scale = Value.accept(binaryOperatorExpression.getOperand2().evaluate(evaluationContext), new IntegerValueVisitor(InstructionSize.LONG, context.encoding, assemblyMessageConsumer)); final int scaleEncoding; if (scale != null) { switch (scale) { case 1: scaleEncoding = 0; break; case 2: scaleEncoding = 1; break; case 4: scaleEncoding = 2; break; case 8: scaleEncoding = 3; break; default: assemblyMessageConsumer.accept(new InvalidScaleValueErrorMessage(scale.intValue())); scaleEncoding = 0; break; } } else { scaleEncoding = 0; } return indexReg | scaleEncoding << 1; } } } else { final int indexReg = parseIndexRegisterName(expression, context); if (indexReg != -1) { return indexReg; } } return -1; } private static int calculateDisplacementSize(int displacement) { if (displacement == 0) { return 0; } if (fitsInWord(displacement)) { return 1; } return 2; } private static void encodeAbsoluteAddressing(@Nonnull Expression expression, @Nonnull AbsoluteAddressingSize size, @Nonnull Set<AddressingMode> validAddressingModes, @Nonnull EvaluationContext evaluationContext, @Nonnull Consumer<AssemblyMessage> assemblyMessageConsumer, @Nonnull EffectiveAddress result, int offsetToExtensionWords, @Nonnull M68KBasicAssemblyContext context) { Integer intValue = Value.accept(expression.evaluate(evaluationContext), new IntegerValueVisitor( size == AbsoluteAddressingSize.WORD ? InstructionSize.WORD : InstructionSize.LONG, context.encoding, assemblyMessageConsumer)); int value = intValue != null ? intValue : 0; if (size == AbsoluteAddressingSize.DEFAULT) { if (fitsInWord(value)) { size = AbsoluteAddressingSize.WORD; } else { if (context.optimizeUnsizedAbsoluteAddressingToPcRelative && validAddressingModes.contains(AddressingMode.PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT) && fitsInWord(value - ((int) context.programCounter + offsetToExtensionWords))) { encodeQuad(true, value, true, 8, false, 0, 0, MemoryIndirectMode.NONE, validAddressingModes, offsetToExtensionWords, context, assemblyMessageConsumer, result); return; } size = AbsoluteAddressingSize.LONG; } } if (size == AbsoluteAddressingSize.WORD) { result.numberOfWords = 2; result.word0 = EA_ABSOLUTE_SHORT_ADDRESSING; result.word1 = (short) value; } else { result.numberOfWords = 3; result.word0 = EA_ABSOLUTE_LONG_ADDRESSING; result.word1 = (short) (value >> 16); result.word2 = (short) value; } validateAddressingMode(validAddressingModes, AddressingMode.ABSOLUTE, assemblyMessageConsumer); } private static void encodeAddressRegisterIndirect(int registerNumber, @Nonnull Set<AddressingMode> validAddressingModes, @Nonnull Consumer<AssemblyMessage> assemblyMessageConsumer, @Nonnull EffectiveAddress result) { if (!validAddressingModes.contains(AddressingMode.ADDRESS_REGISTER_INDIRECT)) { // If (An) is not allowed, but (d16,An) is, use that mode instead. // This is used by the MOVEP instruction. if (validAddressingModes.contains(AddressingMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT)) { result.numberOfWords = 2; result.word0 = (short) (MODE_ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT | registerNumber); result.word1 = 0; return; } assemblyMessageConsumer.accept(new AddressingModeNotAllowedHereErrorMessage()); } result.numberOfWords = 1; result.word0 = (short) (MODE_ADDRESS_REGISTER_INDIRECT | registerNumber); } private static boolean endOfEA(@Nonnull Tokenizer tokenizer, boolean expectBitFieldSpecificationAtEnd) { if (expectBitFieldSpecificationAtEnd) { return tokenizer.getTokenType() == TokenType.OPENING_BRACE; } return tokenizer.getTokenType() == TokenType.END; } private static boolean equalsAsciiCaseInsensitive(char a, char b) { assert b >= 'A' && b <= 'Z'; return a == b || a == (b | 0x20); } private static boolean fitsInByte(int value) { return value >= -0x80 && value <= 0x7F; } private static boolean fitsInWord(int value) { return value >= -0x8000 && value <= 0x7FFF; } private static int parseAddressRegisterIndirect(@Nonnull GroupingExpression expression, @Nonnull M68KBasicAssemblyContext context) { final Expression childExpression = expression.getChildExpression(); if (childExpression instanceof IdentifierExpression) { // Check if the identifier matches a register name. final String identifier = ((IdentifierExpression) childExpression).getIdentifier(); final short reg = identifyDataOrAddressRegister(identifier, false, context); if (reg != -1 && (reg & MODE_MASK) == MODE_ADDRESS_REGISTER_DIRECT) { return reg & REGISTER_MASK; } } return -1; } private static int parseIndexRegisterName(@Nonnull Expression expression, @Nonnull M68KBasicAssemblyContext context) { if (expression instanceof IdentifierExpression) { final String identifier = ((IdentifierExpression) expression).getIdentifier(); final int indexSize = parseIndexRegisterOrAbsoluteAddressingSize(identifier); if (indexSize == -1) { final short reg = identifyDataOrAddressRegister(identifier, true, context); if (reg != -1) { return reg << 4; } } else { final short reg = identifyDataOrAddressRegister(identifier.substring(0, identifier.length() - 2), true, context); if (reg != -1) { return reg << 4 | indexSize << 3; } } } return -1; } private static int parseIndexRegisterOrAbsoluteAddressingSize(@Nonnull String identifier) { if (identifier.length() > 2) { final String suffix = identifier.substring(identifier.length() - 2); if (suffix.charAt(0) == '.') { if (equalsAsciiCaseInsensitive(suffix.charAt(1), 'W')) { return 0; } else if (equalsAsciiCaseInsensitive(suffix.charAt(1), 'L')) { return 1; } } } return -1; } private static void parseQuad(@Nonnull Tokenizer tokenizer, @CheckForNull SymbolLookup symbolLookup, boolean expectBitFieldSpecificationAtEnd, @CheckForNull Expression baseDisplacementExpression, @Nonnull Set<AddressingMode> validAddressingModes, int offsetToExtensionWords, @Nonnull EvaluationContext evaluationContext, @Nonnull M68KBasicAssemblyContext context, @Nonnull Consumer<AssemblyMessage> assemblyMessageConsumer, @Nonnull EffectiveAddress result) throws InvalidTokenException { QuadParser quad = new QuadParser(); quad.baseDisplacementExpression = baseDisplacementExpression; tokenizer.advance(); for (;;) { for (;;) { // not actually a loop if (tokenizer.getTokenType() == TokenType.OPENING_BRACKET) { if (quad.memoryIndirectState != MemoryIndirectState.NONE) { return; } quad.memoryIndirectState = MemoryIndirectState.INCOMPLETE; // If we have a base displacement, move it to the outer displacement. if (quad.baseDisplacementExpression != null) { quad.outerDisplacementExpression = quad.baseDisplacementExpression; quad.baseDisplacementExpression = null; } // If we have a base register, move it to the index register. if (quad.haveBaseRegister) { // If the base register is PC or ZPC, move it to the outer displacement instead. if (quad.baseRegister >= 8) { if (quad.outerDisplacementExpression != null) { return; } quad.outerDisplacementExpression = quad.baseRegisterExpression; } else { quad.haveIndexRegister = true; quad.indexRegister = 0b10000000 | quad.baseRegister << 4; } quad.haveBaseRegister = false; quad.baseRegister = 0; quad.baseRegisterExpression = null; } tokenizer.advance(); if (tokenizer.getTokenType() == TokenType.CLOSING_BRACKET) { break; } } final Expression expression = ExpressionParser.parse(tokenizer, symbolLookup, assemblyMessageConsumer); if (expression == null) { return; } if (!quad.processExpression(expression, evaluationContext, context, assemblyMessageConsumer)) { return; } break; } TokenType tokenType = tokenizer.getTokenType(); if (tokenType == TokenType.CLOSING_BRACKET) { if (quad.memoryIndirectState != MemoryIndirectState.INCOMPLETE) { return; } quad.memoryIndirectState = MemoryIndirectState.COMPLETE; tokenizer.advance(); tokenType = tokenizer.getTokenType(); } if (tokenType == TokenType.CLOSING_PARENTHESIS) { if (quad.memoryIndirectState == MemoryIndirectState.INCOMPLETE) { return; } tokenizer.advance(); break; } if (tokenType != TokenType.COMMA) { return; } tokenizer.advance(); } quad.encode(validAddressingModes, offsetToExtensionWords, evaluationContext, context, assemblyMessageConsumer, result); if (!endOfEA(tokenizer, expectBitFieldSpecificationAtEnd)) { assemblyMessageConsumer.accept(new SyntaxErrorInEffectiveAddressErrorMessage()); } } @Nonnull private static Expression reconstructExpressionWithNewTail(@Nonnull Stack<Expression> expressionStack, @Nonnull Expression newTailExpression) { if (expressionStack != null) { while (!expressionStack.isEmpty()) { final Expression ancestorExpression = expressionStack.pop(); final Expression reconstructedExpression; if (ancestorExpression instanceof UnaryOperatorExpression) { reconstructedExpression = new UnaryOperatorExpression( ((UnaryOperatorExpression) ancestorExpression).getOperator(), newTailExpression); } else if (ancestorExpression instanceof BinaryOperatorExpression) { final BinaryOperatorExpression binOpExpression = (BinaryOperatorExpression) ancestorExpression; reconstructedExpression = new BinaryOperatorExpression(binOpExpression.getOperator(), binOpExpression.getOperand1(), newTailExpression); } else if (ancestorExpression instanceof ConditionalExpression) { final ConditionalExpression condExpression = (ConditionalExpression) ancestorExpression; reconstructedExpression = new ConditionalExpression(condExpression.getCondition(), condExpression.getTruePart(), newTailExpression); } else { throw new RuntimeException("Unexpected expression type for expression: " + ancestorExpression.toString()); // unreachable } newTailExpression = reconstructedExpression; } } return newTailExpression; } private static void validateAddressingMode(@Nonnull Set<AddressingMode> validAddressingModes, @Nonnull AddressingMode addressingMode, @Nonnull Consumer<AssemblyMessage> assemblyMessageConsumer) { if (!validAddressingModes.contains(addressingMode)) { assemblyMessageConsumer.accept(new AddressingModeNotAllowedHereErrorMessage()); } } // word0 through word5 are to be treated as the elements of an array of shorts. numberOfWords specifies the number of // elements in the array. word0 is always in the single effective address operation word format. The next word, if present, // is either in the brief extension word format or the full extension word format. The next words are the base displacement // and/or the outer displacement, in 0, 1 or 2 words each. short numberOfWords; short word0, word1, word2, word3, word4, word5; int getMode() { return this.word0 & MODE_MASK; } int getRegister() { return this.word0 & REGISTER_MASK; } short getWord(int i) { switch (i) { case 0: return this.word0; case 1: return this.word1; case 2: return this.word2; case 3: return this.word3; case 4: return this.word4; case 5: return this.word5; } throw new IndexOutOfBoundsException("expected index between 0 and 5 (inclusive), got " + i); } boolean isAddressRegisterDirect() { return (this.word0 & MODE_MASK) == MODE_ADDRESS_REGISTER_DIRECT; } boolean isAddressRegisterIndirectWithDisplacement() { return (this.word0 & MODE_MASK) == MODE_ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT; } boolean isAddressRegisterIndirectWithPostincrement() { return (this.word0 & MODE_MASK) == MODE_ADDRESS_REGISTER_INDIRECT_WITH_POSTINCREMENT; } boolean isAddressRegisterIndirectWithPredecrement() { return (this.word0 & MODE_MASK) == MODE_ADDRESS_REGISTER_INDIRECT_WITH_PREDECREMENT; } boolean isDataRegisterDirect() { return (this.word0 & MODE_MASK) == MODE_DATA_REGISTER_DIRECT; } boolean isImmediateData() { return (this.word0 & EA_MASK) == EA_IMMEDIATE_DATA; } private void setWord(int i, short value) { switch (i) { case 0: this.word0 = value; return; case 1: this.word1 = value; return; case 2: this.word2 = value; return; case 3: this.word3 = value; return; case 4: this.word4 = value; return; case 5: this.word5 = value; return; } throw new IndexOutOfBoundsException("expected index between 0 and 5 (inclusive), got " + i); } }