package org.reasm.m68k.assembly.internal; import java.io.IOException; import java.nio.ByteBuffer; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import org.reasm.Function; import org.reasm.commons.messages.FunctionCannotBeConvertedToIntegerErrorMessage; import org.reasm.commons.messages.StringTooLongErrorMessage; import org.reasm.commons.messages.ValueOutOfRangeErrorMessage; final class DcIntegerValueVisitor implements DcValueVisitor { @Nonnull private final M68KAssemblyContext context; @Nonnull private InstructionSize size = InstructionSize.DEFAULT; private long output; private boolean signed; @CheckForNull private ByteBuffer outputBytes; DcIntegerValueVisitor(@Nonnull M68KAssemblyContext context) { this.context = context; } @Override public void encode() throws IOException { switch (this.size) { case BYTE: if (this.outputBytes != null) { this.context.builder.appendAssembledData(this.outputBytes); } else { this.context.appendByte((byte) this.output); } break; case DEFAULT: case WORD: default: this.context.appendWord((short) this.output); break; case LONG: this.context.appendLong((int) this.output); break; case QUAD: this.context.appendQuad(this.output); break; } } @Override public void reset(InstructionSize size) { this.size = size; this.output = 0; this.signed = false; this.outputBytes = null; } @Override public Void visitFloat(double value) { return this.visitSignedInt((long) value); } @Override public Void visitFunction(Function value) { this.context.addTentativeMessage(new FunctionCannotBeConvertedToIntegerErrorMessage()); return null; } @Override public Void visitSignedInt(long value) { this.output = value; this.signed = true; this.validate(); return null; } @Override public Void visitString(String value) { final ByteBuffer stringBytes = this.context.encoding.encode(value); int maxLength; switch (this.size) { case BYTE: // DC.B, when given a string operand, outputs the whole string (encoded) this.outputBytes = stringBytes; return null; case DEFAULT: case WORD: default: maxLength = 2; break; case LONG: maxLength = 4; break; case QUAD: maxLength = 8; break; } if (stringBytes.limit() > maxLength) { this.context.addTentativeMessage(new StringTooLongErrorMessage(value)); } if (maxLength > stringBytes.limit()) { maxLength = stringBytes.limit(); } long result = 0; for (; maxLength != 0; maxLength--) { result <<= 8; result |= stringBytes.get() & 0xFF; } this.output = result; this.signed = false; return null; } @Override public Void visitUndetermined() { return null; } @Override public Void visitUnsignedInt(long value) { this.output = value; this.signed = false; this.validate(); return null; } /** * Validates the output value against the size. This is done once while visiting the value because {@link #encode()} may be * called multiple times in a <code>DCB</code> directive. */ private void validate() { switch (this.size) { case BYTE: if (this.signed) { if (this.output < -0x80 || this.output > 0xFF) { this.context.addTentativeMessage(new ValueOutOfRangeErrorMessage(this.output)); } } else { if (this.output < 0 || this.output > 0xFF) { this.context.addTentativeMessage(new ValueOutOfRangeErrorMessage(this.output)); } } break; case WORD: case DEFAULT: default: if (this.signed) { if (this.output < -0x8000 || this.output > 0xFFFF) { this.context.addTentativeMessage(new ValueOutOfRangeErrorMessage(this.output)); } } else { if (this.output < 0 || this.output > 0xFFFF) { this.context.addTentativeMessage(new ValueOutOfRangeErrorMessage(this.output)); } } break; case LONG: if (this.signed) { if (this.output < -0x80000000L || this.output > 0xFFFFFFFFL) { this.context.addTentativeMessage(new ValueOutOfRangeErrorMessage(this.output)); } } else { if (this.output < 0 || this.output > 0xFFFFFFFFL) { this.context.addTentativeMessage(new ValueOutOfRangeErrorMessage(this.output)); } } break; case QUAD: break; } } }