package org.reasm.m68k.assembly.internal;
import java.io.IOException;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;
import org.reasm.m68k.InstructionSet;
import org.reasm.m68k.messages.BreakpointNumberOutOfRangeErrorMessage;
import org.reasm.m68k.messages.TrapVectorOutOfRangeErrorMessage;
/**
* Base class for all instructions that take one operand that is an effective address.
*
* @author Francis Gagné
*/
@Immutable
abstract class OneEaInstruction extends Instruction {
@Immutable
private abstract static class AnySize extends OneEaInstruction {
@Immutable
private static class DataAlterable extends AnySize {
DataAlterable(short fixedBits) {
super(fixedBits);
}
@Override
Set<AddressingMode> getValidAddressingModes(InstructionSet instructionSet) {
return AddressingModeCategory.DATA_ALTERABLE;
}
}
@Immutable
private static class Tst extends AnySize {
Tst() {
super((short) 0b01001010_00000000);
}
@Override
Set<AddressingMode> getValidAddressingModes(InstructionSet instructionSet) {
if (InstructionSetCheck.CPU32_OR_MC68020_OR_LATER.isSupported(instructionSet)) {
return AddressingModeCategory.ALL;
}
return AddressingModeCategory.DATA_ALTERABLE;
}
}
private final short fixedBits;
AnySize(short fixedBits) {
this.fixedBits = fixedBits;
}
@Override
void encode(M68KAssemblyContext context, InstructionSize size, EffectiveAddress ea) {
ea.word0 |= this.fixedBits | encodeIntegerSizeStandard(size);
if (size == InstructionSize.BYTE) {
context.validateForByteAccess(ea);
}
}
}
@Immutable
private static class Ext extends OneEaInstruction {
Ext() {
}
@Override
void encode(M68KAssemblyContext context, InstructionSize size, EffectiveAddress ea) {
final int register = ea.isDataRegisterDirect() ? ea.getRegister() : 0;
ea.word0 = (short) (0b01001000_10000000 | register);
switch (size) {
case BYTE:
context.addInvalidSizeAttributeErrorMessage();
break;
case WORD:
default:
break;
case LONG:
ea.word0 |= 0b00000000_01000000;
break;
}
}
@Override
Set<AddressingMode> getValidAddressingModes(InstructionSet instructionSet) {
return AddressingModeCategory.DATA_REGISTER_DIRECT;
}
}
@Immutable
private static class Extb extends OneEaInstruction {
Extb() {
}
@Override
void encode(M68KAssemblyContext context, InstructionSize size, EffectiveAddress ea) {
final int register = ea.isDataRegisterDirect() ? ea.getRegister() : 0;
ea.word0 = (short) (0b01001001_11000000 | register);
switch (size) {
case BYTE:
case WORD:
context.addInvalidSizeAttributeErrorMessage();
break;
case LONG:
default:
break;
}
}
@Override
InstructionSetCheck getInstructionSetCheck() {
return InstructionSetCheck.CPU32_OR_MC68020_OR_LATER;
}
@Override
Set<AddressingMode> getValidAddressingModes(InstructionSet instructionSet) {
return AddressingModeCategory.DATA_REGISTER_DIRECT;
}
}
@Immutable
private static abstract class Immediate extends OneEaInstruction {
@Immutable
private static class Bkpt extends Immediate {
Bkpt() {
}
@Override
void encode(M68KAssemblyContext context, EffectiveAddress ea, int immediateData) {
final int breakpointNumber = immediateData;
if (breakpointNumber < 0 || breakpointNumber > 7) {
context.addTentativeMessage(new BreakpointNumberOutOfRangeErrorMessage());
}
ea.word0 = (short) (0b01001000_01001000 | breakpointNumber & 7);
ea.numberOfWords = 1;
}
@Override
InstructionSize getDefaultImmediateSize() {
// Accept immediate values of any size; encode() does its own bounds checking.
return InstructionSize.LONG;
}
@Override
InstructionSetCheck getInstructionSetCheck() {
return InstructionSetCheck.MC68EC000_OR_LATER;
}
}
// M68000PRM says LPSTOP is a word-size instruction, but STOP is unsized.
// We implement both as unsized instructions.
@Immutable
private static class Lpstop extends Immediate {
Lpstop() {
}
@Override
void encode(M68KAssemblyContext context, EffectiveAddress ea, int immediateData) {
ea.word0 = (short) 0b11111000_00000000;
ea.word1 = 0b00000001_11000000;
ea.word2 = (short) immediateData;
ea.numberOfWords = 3;
}
@Override
InstructionSetCheck getInstructionSetCheck() {
return InstructionSetCheck.CPU32_ONLY;
}
}
@Immutable
private static class Rtd extends Immediate {
Rtd() {
}
@Override
void encode(M68KAssemblyContext context, EffectiveAddress ea, int immediateData) {
ea.word0 = 0b01001110_01110100;
ea.word1 = (short) immediateData;
ea.numberOfWords = 2;
}
@Override
InstructionSetCheck getInstructionSetCheck() {
return InstructionSetCheck.MC68010_OR_LATER;
}
}
@Immutable
private static class Stop extends Immediate {
Stop() {
}
@Override
void encode(M68KAssemblyContext context, EffectiveAddress ea, int immediateData) {
ea.word0 = 0b01001110_01110010;
ea.word1 = (short) immediateData;
ea.numberOfWords = 2;
}
@Override
InstructionSetCheck getInstructionSetCheck() {
return InstructionSetCheck.M68000_FAMILY;
}
}
@Immutable
private static class Trap extends Immediate {
Trap() {
}
@Override
void encode(M68KAssemblyContext context, EffectiveAddress ea, int immediateData) {
final int trapVector = immediateData;
if (trapVector < 0 || trapVector > 15) {
context.addTentativeMessage(new TrapVectorOutOfRangeErrorMessage());
}
ea.word0 = (short) (0b01001110_01000000 | trapVector & 15);
ea.numberOfWords = 1;
}
@Override
InstructionSize getDefaultImmediateSize() {
// Accept immediate values of any size; encode() does its own bounds checking.
return InstructionSize.LONG;
}
}
Immediate() {
}
abstract void encode(@Nonnull M68KAssemblyContext context, @Nonnull EffectiveAddress ea, int immediateData);
@Override
final void encode(M68KAssemblyContext context, InstructionSize size, EffectiveAddress ea) {
final int immediateData;
if (ea.isImmediateData()) {
if (ea.numberOfWords == 2) {
immediateData = ea.word1;
} else {
immediateData = ea.word1 << 16 | ea.word2;
}
} else {
immediateData = 0;
}
this.encode(context, ea, immediateData);
if (size != InstructionSize.DEFAULT) {
context.addInvalidSizeAttributeErrorMessage();
}
}
@Override
final Set<AddressingMode> getValidAddressingModes(InstructionSet instructionSet) {
return AddressingModeCategory.IMMEDIATE_DATA;
}
}
@Immutable
private static class Jump extends OneEaInstruction {
private final int instruction;
Jump(int instruction) {
this.instruction = instruction;
}
@Override
void encode(M68KAssemblyContext context, InstructionSize size, EffectiveAddress ea) {
ea.word0 |= 0b01001110_10000000 | this.instruction;
if (size != InstructionSize.DEFAULT) {
context.addInvalidSizeAttributeErrorMessage();
}
}
@Override
Set<AddressingMode> getValidAddressingModes(InstructionSet instructionSet) {
return AddressingModeCategory.CONTROL;
}
}
@Immutable
private static class Nbcd extends OneEaInstruction {
Nbcd() {
}
@Override
void encode(M68KAssemblyContext context, InstructionSize size, EffectiveAddress ea) {
ea.word0 |= 0b01001000_00000000;
switch (size) {
case BYTE:
default:
break;
case WORD:
case LONG:
context.addInvalidSizeAttributeErrorMessage();
break;
}
}
@Override
Set<AddressingMode> getValidAddressingModes(InstructionSet instructionSet) {
return AddressingModeCategory.DATA_ALTERABLE;
}
}
@Immutable
private static class Pea extends OneEaInstruction {
Pea() {
}
@Override
void encode(M68KAssemblyContext context, InstructionSize size, EffectiveAddress ea) {
ea.word0 |= 0b01001000_01000000;
switch (size) {
case BYTE:
case WORD:
context.addInvalidSizeAttributeErrorMessage();
break;
case LONG:
default:
break;
}
}
@Override
Set<AddressingMode> getValidAddressingModes(InstructionSet instructionSet) {
return AddressingModeCategory.CONTROL;
}
}
@Immutable
private static class Rtm extends OneEaInstruction {
Rtm() {
}
@Override
void encode(M68KAssemblyContext context, InstructionSize size, EffectiveAddress ea) {
ea.word0 |= 0b00000110_11000000;
if (size != InstructionSize.DEFAULT) {
context.addInvalidSizeAttributeErrorMessage();
}
}
@Override
InstructionSetCheck getInstructionSetCheck() {
return InstructionSetCheck.MC68020_ONLY;
}
@Override
Set<AddressingMode> getValidAddressingModes(InstructionSet instructionSet) {
return AddressingModeCategory.DATA_OR_ADDRESS_REGISTER_DIRECT;
}
}
@Immutable
private static class Scc extends OneEaInstruction {
@Nonnull
private final IntegerConditionCode cc;
Scc(@Nonnull IntegerConditionCode cc) {
this.cc = cc;
}
@Override
void encode(M68KAssemblyContext context, InstructionSize size, EffectiveAddress ea) {
ea.word0 |= 0b01010000_11000000 | this.cc.ordinal() << 8;
switch (size) {
case BYTE:
default:
break;
case WORD:
case LONG:
context.addInvalidSizeAttributeErrorMessage();
break;
}
}
@Override
Set<AddressingMode> getValidAddressingModes(InstructionSet instructionSet) {
return AddressingModeCategory.DATA_ALTERABLE;
}
}
@Immutable
private static class Swap extends OneEaInstruction {
Swap() {
}
@Override
void encode(M68KAssemblyContext context, InstructionSize size, EffectiveAddress ea) {
final int register = ea.isDataRegisterDirect() ? ea.getRegister() : 0;
ea.word0 = (short) (0b01001000_01000000 | register);
switch (size) {
case BYTE:
case LONG:
context.addInvalidSizeAttributeErrorMessage();
break;
case WORD:
default:
break;
}
}
@Override
Set<AddressingMode> getValidAddressingModes(InstructionSet instructionSet) {
return AddressingModeCategory.DATA_REGISTER_DIRECT;
}
}
@Immutable
private static class Tas extends OneEaInstruction {
Tas() {
}
@Override
void encode(M68KAssemblyContext context, InstructionSize size, EffectiveAddress ea) {
ea.word0 |= 0b01001010_11000000;
switch (size) {
case BYTE:
default:
break;
case WORD:
case LONG:
context.addInvalidSizeAttributeErrorMessage();
break;
}
}
@Override
Set<AddressingMode> getValidAddressingModes(InstructionSet instructionSet) {
return AddressingModeCategory.DATA_ALTERABLE;
}
}
@Immutable
private static class Unlk extends OneEaInstruction {
Unlk() {
}
@Override
void encode(M68KAssemblyContext context, InstructionSize size, EffectiveAddress ea) {
ea.word0 |= 0b01001110_01011000;
if (size != InstructionSize.DEFAULT) {
context.addInvalidSizeAttributeErrorMessage();
}
}
@Override
Set<AddressingMode> getValidAddressingModes(InstructionSet instructionSet) {
return AddressingModeCategory.ADDRESS_REGISTER_DIRECT;
}
}
@Nonnull
static final OneEaInstruction BKPT = new Immediate.Bkpt();
@Nonnull
static final OneEaInstruction CLR = new AnySize.DataAlterable((short) 0b01000010_00000000);
@Nonnull
static final OneEaInstruction EXT = new Ext();
@Nonnull
static final OneEaInstruction EXTB = new Extb();
@Nonnull
static final OneEaInstruction JMP = new Jump(0b00000000_01000000);
@Nonnull
static final OneEaInstruction JSR = new Jump(0b00000000_00000000);
@Nonnull
static final OneEaInstruction LPSTOP = new Immediate.Lpstop();
@Nonnull
static final OneEaInstruction NBCD = new Nbcd();
@Nonnull
static final OneEaInstruction NEG = new AnySize.DataAlterable((short) 0b01000100_00000000);
@Nonnull
static final OneEaInstruction NEGX = new AnySize.DataAlterable((short) 0b01000000_00000000);
@Nonnull
static final OneEaInstruction NOT = new AnySize.DataAlterable((short) 0b01000110_00000000);
@Nonnull
static final OneEaInstruction PEA = new Pea();
@Nonnull
static final OneEaInstruction RTD = new Immediate.Rtd();
@Nonnull
static final OneEaInstruction RTM = new Rtm();
@Nonnull
static final OneEaInstruction SCC = new Scc(IntegerConditionCode.CC);
@Nonnull
static final OneEaInstruction SCS = new Scc(IntegerConditionCode.CS);
@Nonnull
static final OneEaInstruction SEQ = new Scc(IntegerConditionCode.EQ);
@Nonnull
static final OneEaInstruction SF = new Scc(IntegerConditionCode.F);
@Nonnull
static final OneEaInstruction SGE = new Scc(IntegerConditionCode.GE);
@Nonnull
static final OneEaInstruction SGT = new Scc(IntegerConditionCode.GT);
@Nonnull
static final OneEaInstruction SHI = new Scc(IntegerConditionCode.HI);
@Nonnull
static final OneEaInstruction SHS = new Scc(IntegerConditionCode.HS);
@Nonnull
static final OneEaInstruction SLE = new Scc(IntegerConditionCode.LE);
@Nonnull
static final OneEaInstruction SLO = new Scc(IntegerConditionCode.LO);
@Nonnull
static final OneEaInstruction SLS = new Scc(IntegerConditionCode.LS);
@Nonnull
static final OneEaInstruction SLT = new Scc(IntegerConditionCode.LT);
@Nonnull
static final OneEaInstruction SMI = new Scc(IntegerConditionCode.MI);
@Nonnull
static final OneEaInstruction SNE = new Scc(IntegerConditionCode.NE);
@Nonnull
static final OneEaInstruction SPL = new Scc(IntegerConditionCode.PL);
@Nonnull
static final OneEaInstruction ST = new Scc(IntegerConditionCode.T);
@Nonnull
static final OneEaInstruction STOP = new Immediate.Stop();
@Nonnull
static final OneEaInstruction SVC = new Scc(IntegerConditionCode.VC);
@Nonnull
static final OneEaInstruction SVS = new Scc(IntegerConditionCode.VS);
@Nonnull
static final OneEaInstruction SWAP = new Swap();
@Nonnull
static final OneEaInstruction TAS = new Tas();
@Nonnull
static final OneEaInstruction TRAP = new Immediate.Trap();
@Nonnull
static final OneEaInstruction TST = new AnySize.Tst();
@Nonnull
static final OneEaInstruction UNLK = new Unlk();
@Override
void assemble2(M68KAssemblyContext context) throws IOException {
InstructionSize size = context.parseIntegerInstructionSize();
if (size == InstructionSize.INVALID) {
context.addInvalidSizeAttributeErrorMessage();
size = InstructionSize.DEFAULT;
}
InstructionSize immediateSize = size;
if (size == InstructionSize.DEFAULT) {
immediateSize = this.getDefaultImmediateSize();
}
if (context.requireNumberOfOperands(1)) {
final EffectiveAddress ea = context.ea0;
context.getEffectiveAddress(context.getOperandText(0), this.getValidAddressingModes(context.instructionSet),
immediateSize, ea);
this.encode(context, size, ea);
context.appendEffectiveAddress(ea);
}
}
@Override
void checkInstructionSet(M68KAssemblyContext context) {
checkInstructionSet(this.getInstructionSetCheck(), context);
}
abstract void encode(@Nonnull M68KAssemblyContext context, @Nonnull InstructionSize size, @Nonnull EffectiveAddress ea);
@Nonnull
InstructionSize getDefaultImmediateSize() {
return InstructionSize.WORD;
}
@Nonnull
InstructionSetCheck getInstructionSetCheck() {
return InstructionSetCheck.M68000_FAMILY;
}
@Nonnull
abstract Set<AddressingMode> getValidAddressingModes(@Nonnull InstructionSet instructionSet);
}