Massively reworked and extended ISA-Description class.

Added inspection to validate the correctness of a MC68000 instruction regarding operation size and address modes.
This commit is contained in:
Chris Hodges 2021-07-31 13:56:07 +02:00
parent 128330d2c7
commit 3aeb415974
17 changed files with 993 additions and 103 deletions

View File

@ -71,12 +71,13 @@ make it work with JUnit 5. Feel free to use the code (in package ```de.platon42.
### V0.4 (unreleased) ### V0.4 (unreleased)
- Enhancement: Added Structure View filters. - Enhancement: Added Structure View filters.
- New: Added inspection to validate the correctness of a MC68000 instruction regarding operation size and address modes.
### V0.3 (28-Jul-21) ### V0.3 (28-Jul-21)
- Enhancement: Macro contents are no longer parsed, added syntax highlighting options for macros. - Enhancement: Macro contents are no longer parsed, added syntax highlighting options for macros.
- Enhancement: Macro definitions are now word and stub indexed, macro calls reference to definition. - Enhancement: Macro definitions are now word and stub indexed, macro calls reference to definition.
- Enhancement: Macro definition refactoring and find usages support. - New: Macro definition refactoring and find usages support.
- Enhancement: Structural View also shows macro definitions. - Enhancement: Structural View also shows macro definitions.
- Bugfix: Missing REPT and ENDR assembler directives added. - Bugfix: Missing REPT and ENDR assembler directives added.
- Cosmetics: Changed or added some icons at various places. - Cosmetics: Changed or added some icons at various places.

View File

@ -7,7 +7,7 @@ plugins {
} }
group = 'de.platon42' group = 'de.platon42'
version = '0.3' version = '0.4'
sourceCompatibility = "1.8" sourceCompatibility = "1.8"
targetCompatibility = "1.8" targetCompatibility = "1.8"
@ -60,12 +60,13 @@ patchPluginXml {
<h4>V0.4 (unreleased)</h4> <h4>V0.4 (unreleased)</h4>
<ul> <ul>
<li>Enhancement: Added Structure View filters. <li>Enhancement: Added Structure View filters.
<li>New: Added inspection to validate the correctness of a MC68000 instruction regarding operation size and address modes.
</ul> </ul>
<h4>V0.3 (28-Jul-21)</h4> <h4>V0.3 (28-Jul-21)</h4>
<ul> <ul>
<li>Enhancement: Macro contents are no longer parsed, added syntax highlighting options for macros. <li>Enhancement: Macro contents are no longer parsed, added syntax highlighting options for macros.
<li>Enhancement: Macro definitions are now word and stub indexed, macro calls reference to definition. <li>Enhancement: Macro definitions are now word and stub indexed, macro calls reference to definition.
<li>Enhancement: Macro definition refactoring and find usages support. <li>New: Macro definition refactoring and find usages support.
<li>Enhancement: Structural View also shows macro definitions. <li>Enhancement: Structural View also shows macro definitions.
<li>Bugfix: Missing REPT and ENDR assembler directives added. <li>Bugfix: Missing REPT and ENDR assembler directives added.
<li>Cosmetics: Changed or added some icons at various places. <li>Cosmetics: Changed or added some icons at various places.

View File

@ -364,12 +364,12 @@ public class M68kParser implements PsiParser, LightPsiParser {
// MNEMONIC OperandSize? // MNEMONIC OperandSize?
public static boolean AsmOp(PsiBuilder b, int l) { public static boolean AsmOp(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "AsmOp")) return false; if (!recursion_guard_(b, l, "AsmOp")) return false;
if (!nextTokenIs(b, MNEMONIC)) return false; if (!nextTokenIs(b, "<mnemonic>", MNEMONIC)) return false;
boolean r; boolean r;
Marker m = enter_section_(b); Marker m = enter_section_(b, l, _NONE_, ASM_OP, "<mnemonic>");
r = consumeToken(b, MNEMONIC); r = consumeToken(b, MNEMONIC);
r = r && AsmOp_1(b, l + 1); r = r && AsmOp_1(b, l + 1);
exit_section_(b, m, ASM_OP, r); exit_section_(b, l, m, r, false, null);
return r; return r;
} }

View File

@ -1,6 +1,7 @@
// This is a generated file. Not intended for manual editing. // This is a generated file. Not intended for manual editing.
package de.platon42.intellij.plugins.m68k.psi; package de.platon42.intellij.plugins.m68k.psi;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public interface M68kAsmOp extends M68kPsiElement { public interface M68kAsmOp extends M68kPsiElement {
@ -8,4 +9,9 @@ public interface M68kAsmOp extends M68kPsiElement {
@Nullable @Nullable
M68kOperandSize getOperandSize(); M68kOperandSize getOperandSize();
@NotNull
String getMnemonic();
int getOpSize();
} }

View File

@ -3,4 +3,6 @@ package de.platon42.intellij.plugins.m68k.psi;
public interface M68kOperandSize extends M68kPsiElement { public interface M68kOperandSize extends M68kPsiElement {
int getSize();
} }

View File

@ -7,6 +7,7 @@ import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
import de.platon42.intellij.plugins.m68k.psi.M68kAsmOp; import de.platon42.intellij.plugins.m68k.psi.M68kAsmOp;
import de.platon42.intellij.plugins.m68k.psi.M68kOperandSize; import de.platon42.intellij.plugins.m68k.psi.M68kOperandSize;
import de.platon42.intellij.plugins.m68k.psi.M68kPsiImplUtil;
import de.platon42.intellij.plugins.m68k.psi.M68kVisitor; import de.platon42.intellij.plugins.m68k.psi.M68kVisitor;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -33,4 +34,15 @@ public class M68kAsmOpImpl extends ASTWrapperPsiElement implements M68kAsmOp {
return PsiTreeUtil.getChildOfType(this, M68kOperandSize.class); return PsiTreeUtil.getChildOfType(this, M68kOperandSize.class);
} }
@Override
@NotNull
public String getMnemonic() {
return M68kPsiImplUtil.getMnemonic(this);
}
@Override
public int getOpSize() {
return M68kPsiImplUtil.getOpSize(this);
}
} }

View File

@ -5,6 +5,7 @@ import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode; import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiElementVisitor;
import de.platon42.intellij.plugins.m68k.psi.M68kOperandSize; import de.platon42.intellij.plugins.m68k.psi.M68kOperandSize;
import de.platon42.intellij.plugins.m68k.psi.M68kPsiImplUtil;
import de.platon42.intellij.plugins.m68k.psi.M68kVisitor; import de.platon42.intellij.plugins.m68k.psi.M68kVisitor;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -24,4 +25,9 @@ public class M68kOperandSizeImpl extends ASTWrapperPsiElement implements M68kOpe
else super.accept(visitor); else super.accept(visitor);
} }
@Override
public int getSize() {
return M68kPsiImplUtil.getSize(this);
}
} }

View File

@ -9,6 +9,39 @@ enum class Machine {
MC68060 MC68060
} }
enum class AddressMode {
IMMEDIATE_DATA,
ADDRESS_REGISTER_INDIRECT_PRE_DEC,
ADDRESS_REGISTER_INDIRECT_POST_INC,
ADDRESS_REGISTER_INDIRECT,
ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT,
ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
PROGRAM_COUNTER_INDIRECT_WITH_INDEX,
SPECIAL_REGISTER_DIRECT,
DATA_REGISTER_DIRECT,
ADDRESS_REGISTER_DIRECT,
REGISTER_LIST,
ABSOLUTE_ADDRESS
}
const val OP_UNSIZED = 0
const val OP_SIZE_B = 1
const val OP_SIZE_W = 2
const val OP_SIZE_L = 4
const val OP_SIZE_S = 8
const val OP_SIZE_BWL = (OP_SIZE_B or OP_SIZE_W or OP_SIZE_L)
const val OP_SIZE_WL = (OP_SIZE_W or OP_SIZE_L)
const val OP_SIZE_SBW = (OP_SIZE_B or OP_SIZE_S or OP_SIZE_W)
data class AllowedAdrMode(
val op1: Set<AddressMode>? = null,
val op2: Set<AddressMode>? = null,
val size: Int = OP_SIZE_BWL,
val specialReg: String? = null
)
data class IsaData( data class IsaData(
val mnemonic: String, val mnemonic: String,
val description: String, val description: String,
@ -16,141 +49,502 @@ data class IsaData(
val altMnemonics: List<String> = emptyList(), val altMnemonics: List<String> = emptyList(),
val conditionCodes: List<String> = emptyList(), val conditionCodes: List<String> = emptyList(),
val id: String = mnemonic, val id: String = mnemonic,
val opSize: String = "bWl",
val isPrivileged: Boolean = false, val isPrivileged: Boolean = false,
val hasOps: Boolean = true val hasOps: Boolean = true,
val modes: List<AllowedAdrMode> = listOf(AllowedAdrMode())
) )
object M68kIsa { object M68kIsa {
val conditionCodes =
listOf("cc", "ls", "cs", "lt", "eq", "mi", "f", "ne", "ge", "pl", "gt", "t", "hi", "vc", "le", "vs")
val conditionCodesBcc = conditionCodes.filterNot { it == "f" || it == "t" }
val isaData = listOf( private val NO_OPS_UNSIZED = listOf(AllowedAdrMode(size = OP_UNSIZED))
private val ALL_68000_MODES = setOf(
AddressMode.DATA_REGISTER_DIRECT,
AddressMode.ADDRESS_REGISTER_DIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC,
AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
AddressMode.ABSOLUTE_ADDRESS,
AddressMode.IMMEDIATE_DATA,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX,
)
private val ALL_EXCEPT_IMMEDIATE_AND_PC_REL = setOf(
AddressMode.DATA_REGISTER_DIRECT,
AddressMode.ADDRESS_REGISTER_DIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC,
AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
AddressMode.ABSOLUTE_ADDRESS
)
private val ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL = setOf(
AddressMode.DATA_REGISTER_DIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC,
AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
AddressMode.ABSOLUTE_ADDRESS
)
private val ALL_EXCEPT_AREG_AND_IMMEDIATE = setOf(
AddressMode.DATA_REGISTER_DIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC,
AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
AddressMode.ABSOLUTE_ADDRESS,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX,
)
private val ALL_EXCEPT_AREG = setOf(
AddressMode.DATA_REGISTER_DIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC,
AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
AddressMode.ABSOLUTE_ADDRESS,
AddressMode.IMMEDIATE_DATA,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX,
)
private val INDIRECT_MODES = setOf(
AddressMode.ADDRESS_REGISTER_INDIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC,
AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
AddressMode.ABSOLUTE_ADDRESS
)
private val AREG_ONLY = setOf(AddressMode.ADDRESS_REGISTER_DIRECT)
private val DREG_ONLY = setOf(AddressMode.DATA_REGISTER_DIRECT)
private val ADD_SUB_MODES = listOf(
AllowedAdrMode(ALL_EXCEPT_AREG, setOf(AddressMode.DATA_REGISTER_DIRECT)),
AllowedAdrMode(setOf(AddressMode.ADDRESS_REGISTER_DIRECT), setOf(AddressMode.DATA_REGISTER_DIRECT), OP_SIZE_WL),
AllowedAdrMode(setOf(AddressMode.DATA_REGISTER_DIRECT), INDIRECT_MODES),
)
private val ASD_LSD_ROD_ROXD_MODES = listOf(
AllowedAdrMode(DREG_ONLY, DREG_ONLY),
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), DREG_ONLY),
AllowedAdrMode(INDIRECT_MODES, null),
)
private val BTST_BCHG_BCLR_BSET_MODES = listOf(
AllowedAdrMode(DREG_ONLY, ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, OP_SIZE_L),
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), DREG_ONLY, OP_SIZE_L),
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), INDIRECT_MODES, OP_SIZE_B),
)
private val conditionCodes =
listOf("cc", "ls", "cs", "lt", "eq", "mi", "f", "ne", "ge", "pl", "gt", "t", "hi", "vc", "le", "vs")
private val conditionCodesBcc = conditionCodes.filterNot { it == "f" || it == "t" }
private val isaData = listOf(
// Data Movement Instructions // Data Movement Instructions
IsaData("move", "Move"), IsaData(
IsaData("movea", "Move Address", altMnemonics = listOf("move"), opSize = "L"), "move", "Move",
IsaData("movem", "Move Multiple Registers", opSize = "Wl"), modes = listOf(AllowedAdrMode(ALL_68000_MODES, ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL))
IsaData("movep", "Move Peripheral", opSize = ""), ),
IsaData("moveq", "Move Quick", opSize = "L"), IsaData(
"movea", "Move Address", altMnemonics = listOf("move"),
modes = listOf(AllowedAdrMode(ALL_68000_MODES, AREG_ONLY, OP_SIZE_WL))
),
IsaData(
"movem", "Move Multiple Registers",
modes = listOf(
AllowedAdrMode(
setOf(AddressMode.REGISTER_LIST),
setOf(
AddressMode.ADDRESS_REGISTER_INDIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
AddressMode.ABSOLUTE_ADDRESS
),
OP_SIZE_WL
),
AllowedAdrMode(
setOf(
AddressMode.ADDRESS_REGISTER_INDIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
AddressMode.ABSOLUTE_ADDRESS
),
setOf(AddressMode.REGISTER_LIST),
OP_SIZE_WL
),
// according to Yann, specifying the registers as bitmask is also valid
AllowedAdrMode(
setOf(AddressMode.IMMEDIATE_DATA),
setOf(
AddressMode.ADDRESS_REGISTER_INDIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
AddressMode.ABSOLUTE_ADDRESS
),
OP_SIZE_WL
),
AllowedAdrMode(
setOf(
AddressMode.ADDRESS_REGISTER_INDIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
AddressMode.ABSOLUTE_ADDRESS
),
setOf(AddressMode.IMMEDIATE_DATA),
OP_SIZE_WL
),
)
),
IsaData(
"movep", "Move Peripheral",
modes = listOf(
AllowedAdrMode(
DREG_ONLY,
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT, AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT),
OP_SIZE_WL
),
AllowedAdrMode(
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT, AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT),
DREG_ONLY,
OP_SIZE_WL
)
)
),
IsaData("exg", "Exchange Registers", opSize = "L"), IsaData(
IsaData("lea", "Load Effective Address", opSize = ""), "moveq", "Move Quick",
IsaData("pea", "Push Effective Address", opSize = ""), modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), DREG_ONLY, OP_SIZE_L))
IsaData("link", "Link and Allocate", opSize = ""), ),
IsaData("unlk", "Unlink", opSize = ""),
IsaData(
"exg", "Exchange Registers",
modes = listOf(
AllowedAdrMode(
setOf(AddressMode.DATA_REGISTER_DIRECT, AddressMode.ADDRESS_REGISTER_DIRECT),
setOf(AddressMode.DATA_REGISTER_DIRECT, AddressMode.ADDRESS_REGISTER_DIRECT),
OP_SIZE_L
)
)
),
IsaData(
"lea", "Load Effective Address",
modes = listOf(
AllowedAdrMode(
setOf(
AddressMode.ADDRESS_REGISTER_INDIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
AddressMode.ABSOLUTE_ADDRESS,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX,
),
AREG_ONLY,
OP_SIZE_L
)
)
),
IsaData(
"pea", "Push Effective Address",
modes = listOf(
AllowedAdrMode(
setOf(
AddressMode.ADDRESS_REGISTER_INDIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
AddressMode.ABSOLUTE_ADDRESS,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX,
),
null,
OP_SIZE_L
)
)
),
IsaData(
"link", "Link and Allocate",
modes = listOf(AllowedAdrMode(AREG_ONLY, setOf(AddressMode.IMMEDIATE_DATA), OP_SIZE_W))
),
IsaData(
"unlk", "Unlink",
modes = listOf(AllowedAdrMode(AREG_ONLY, null, OP_UNSIZED))
),
// Integer Arithmetic Instructions // Integer Arithmetic Instructions
IsaData("add", "Add"), IsaData("add", "Add", modes = ADD_SUB_MODES),
IsaData("adda", "Add Address", altMnemonics = listOf("add"), opSize = "Wl"), IsaData("adda", "Add Address", altMnemonics = listOf("add"), modes = listOf(AllowedAdrMode(ALL_68000_MODES, AREG_ONLY, OP_SIZE_WL))),
IsaData("addi", "Add Immediate", altMnemonics = listOf("add")), IsaData(
IsaData("addq", "Add Quick"), "addi", "Add Immediate", altMnemonics = listOf("add"),
IsaData("addx", "Add with Extend"), modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL))
),
IsaData(
"addq", "Add Quick", modes = listOf(
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL),
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), AREG_ONLY, size = OP_SIZE_WL)
)
),
IsaData(
"addx", "Add with Extend",
modes = listOf(
AllowedAdrMode(DREG_ONLY, DREG_ONLY),
AllowedAdrMode(setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC), setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC))
)
),
IsaData("sub", "Subtract"), IsaData("sub", "Subtract", modes = ADD_SUB_MODES),
IsaData("suba", "Subtract Address", altMnemonics = listOf("sub")), IsaData("suba", "Subtract Address", altMnemonics = listOf("sub"), modes = listOf(AllowedAdrMode(ALL_68000_MODES, AREG_ONLY, OP_SIZE_WL))),
IsaData("subi", "Subtract Immediate", altMnemonics = listOf("sub")), IsaData(
IsaData("subq", "Subtract Quick"), "subi", "Subtract Immediate", altMnemonics = listOf("sub"),
IsaData("subx", "Subtract with Extend"), modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL))
),
IsaData(
"subq", "Subtract Quick", modes = listOf(
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL),
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), AREG_ONLY, size = OP_SIZE_WL)
)
),
IsaData(
"subx", "Subtract with Extend",
modes = listOf(
AllowedAdrMode(DREG_ONLY, DREG_ONLY),
AllowedAdrMode(setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC), setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC))
)
),
IsaData("neg", "Negate"), IsaData("neg", "Negate", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null))),
IsaData("negx", "Negate with Extend"), IsaData("negx", "Negate with Extend", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null))),
IsaData("clr", "Clear"), IsaData("clr", "Clear", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null))),
IsaData("cmp", "Compare"), IsaData(
IsaData("cmpa", "Compare Address", altMnemonics = listOf("cmp"), opSize = "Wl"), "cmp", "Compare", modes = listOf(
IsaData("cmpi", "Compare Immediate", altMnemonics = listOf("cmp")), AllowedAdrMode(ALL_EXCEPT_AREG, setOf(AddressMode.DATA_REGISTER_DIRECT)),
IsaData("cmpm", "Compare Memory to Memory", altMnemonics = listOf("cmp")), AllowedAdrMode(setOf(AddressMode.ADDRESS_REGISTER_DIRECT), setOf(AddressMode.DATA_REGISTER_DIRECT), OP_SIZE_WL),
)
),
IsaData("cmpa", "Compare Address", altMnemonics = listOf("cmp"), modes = listOf(AllowedAdrMode(ALL_68000_MODES, AREG_ONLY, OP_SIZE_WL))),
IsaData(
"cmpi", "Compare Immediate", altMnemonics = listOf("cmp"),
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_AND_IMMEDIATE))
),
IsaData(
"cmpm", "Compare Memory to Memory", altMnemonics = listOf("cmp"),
modes = listOf(AllowedAdrMode(setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC), setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC)))
),
IsaData("muls", "Signed Multiply", opSize = "W"), IsaData("muls", "Signed Multiply", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W))),
IsaData("mulu", "Unsigned Multiply", opSize = "W"), IsaData("mulu", "Unsigned Multiply", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W))),
IsaData("divs", "Signed Divide", opSize = "W"), IsaData("divs", "Signed Divide", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W))),
IsaData("divu", "Unsigned Divide", opSize = "W"), IsaData("divu", "Unsigned Divide", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W))),
IsaData("ext", "Sign Extend", opSize = "Wl"), IsaData("ext", "Sign Extend", modes = listOf(AllowedAdrMode(DREG_ONLY, null, OP_SIZE_WL))),
// Logical Instructions // Logical Instructions
IsaData("and", "Logical AND"), IsaData(
IsaData("andi", "Logical AND Immediate", altMnemonics = listOf("and")), "and", "Logical AND",
IsaData("eor", "Logical Exclusive-OR"), modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY), AllowedAdrMode(DREG_ONLY, INDIRECT_MODES))
IsaData("eori", "Logical Exclusive-OR Immediate", altMnemonics = listOf("eor")), ),
IsaData("not", "Logical Complement"), IsaData(
IsaData("or", "Logical Inclusive-OR"), "andi", "Logical AND Immediate", altMnemonics = listOf("and"),
IsaData("ori", "Logical Inclusive-OR Immediate", altMnemonics = listOf("or")), modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL))
),
IsaData(
"eor", "Logical Exclusive-OR",
modes = listOf(AllowedAdrMode(DREG_ONLY, ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL))
),
IsaData(
"eori", "Logical Exclusive-OR Immediate", altMnemonics = listOf("eor"),
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL))
),
IsaData(
"or", "Logical Inclusive-OR",
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY), AllowedAdrMode(DREG_ONLY, INDIRECT_MODES))
),
IsaData(
"ori", "Logical Inclusive-OR Immediate", altMnemonics = listOf("or"),
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL))
),
IsaData(
"not", "Logical Complement",
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null))
),
// Shift and Rotate Instructions // Shift and Rotate Instructions
IsaData("asl", "Arithmetic Shift Left"), IsaData("asl", "Arithmetic Shift Left", modes = ASD_LSD_ROD_ROXD_MODES),
IsaData("asr", "Arithmetic Shift Right"), IsaData("asr", "Arithmetic Shift Right", modes = ASD_LSD_ROD_ROXD_MODES),
IsaData("lsl", "Logical Shift Left"), IsaData("lsl", "Logical Shift Left", modes = ASD_LSD_ROD_ROXD_MODES),
IsaData("lsr", "Logical Shift Right"), IsaData("lsr", "Logical Shift Right", modes = ASD_LSD_ROD_ROXD_MODES),
IsaData("rol", "Rotate Left"), IsaData("rol", "Rotate Left", modes = ASD_LSD_ROD_ROXD_MODES),
IsaData("ror", "Rotate Right"), IsaData("ror", "Rotate Right", modes = ASD_LSD_ROD_ROXD_MODES),
IsaData("roxl", "Rotate with Extend Left"), IsaData("roxl", "Rotate with Extend Left", modes = ASD_LSD_ROD_ROXD_MODES),
IsaData("roxr", "Rotate with Extend Right"), IsaData("roxr", "Rotate with Extend Right", modes = ASD_LSD_ROD_ROXD_MODES),
IsaData("swap", "Swap Register Words", opSize = ""), IsaData("swap", "Swap Register Words", modes = listOf(AllowedAdrMode(DREG_ONLY, null, OP_SIZE_W))),
// Bit Manipulation Instructions // Bit Manipulation Instructions
IsaData("bchg", "Test Bit and Change", opSize = "Bl"), IsaData("bchg", "Test Bit and Change", modes = BTST_BCHG_BCLR_BSET_MODES),
IsaData("bclr", "Test Bit and Clear", opSize = "Bl"), IsaData("bclr", "Test Bit and Clear", modes = BTST_BCHG_BCLR_BSET_MODES),
IsaData("bset", "Test Bit and Set", opSize = "Bl"), IsaData("bset", "Test Bit and Set", modes = BTST_BCHG_BCLR_BSET_MODES),
IsaData("btst", "Test Bit", opSize = "Bl"), IsaData("btst", "Test Bit", modes = BTST_BCHG_BCLR_BSET_MODES),
// Binary-Coded Decimal Instructions // Binary-Coded Decimal Instructions
IsaData("abcd", "Add Decimal with Extend", opSize = ""), IsaData(
IsaData("sbcd", "Subtract Decimal with Extend", opSize = ""), "abcd", "Add Decimal with Extend",
IsaData("nbcd", "Negate Decimal with Extend", opSize = ""), modes = listOf(
AllowedAdrMode(DREG_ONLY, DREG_ONLY, OP_SIZE_B),
AllowedAdrMode(setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC), setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC), OP_SIZE_B)
)
),
IsaData(
"sbcd", "Subtract Decimal with Extend",
modes = listOf(
AllowedAdrMode(DREG_ONLY, DREG_ONLY, OP_SIZE_B),
AllowedAdrMode(setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC), setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC), OP_SIZE_B)
)
),
IsaData(
"nbcd", "Negate Decimal with Extend",
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, OP_SIZE_B))
),
// Program Control Instructions // Program Control Instructions
IsaData("bCC", "Branch Conditionally", conditionCodes = conditionCodesBcc, opSize = "bsW"), IsaData(
IsaData("bra", "Branch", opSize = "bsW"), "bCC", "Branch Conditionally", conditionCodes = conditionCodesBcc,
IsaData("bsr", "Branch to Subroutine", opSize = "bsW"), modes = listOf(AllowedAdrMode(setOf(AddressMode.ABSOLUTE_ADDRESS), null, OP_SIZE_SBW))
),
IsaData("bra", "Branch", modes = listOf(AllowedAdrMode(setOf(AddressMode.ABSOLUTE_ADDRESS), null, OP_SIZE_SBW))),
IsaData("bsr", "Branch to Subroutine", modes = listOf(AllowedAdrMode(setOf(AddressMode.ABSOLUTE_ADDRESS), null, OP_SIZE_SBW))),
IsaData( IsaData(
"dbCC", "dbCC",
"Test Condition, Decrement, and Branch", "Test Condition, Decrement, and Branch",
altMnemonics = listOf("dbra"), altMnemonics = listOf("dbra"),
conditionCodes = conditionCodes, opSize = "W" conditionCodes = conditionCodes,
modes = listOf(AllowedAdrMode(DREG_ONLY, setOf(AddressMode.ABSOLUTE_ADDRESS), OP_SIZE_W))
),
IsaData(
"sCC", "Set Conditionally", conditionCodes = conditionCodes,
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, OP_SIZE_B))
), ),
IsaData("sCC", "Set Conditionally", conditionCodes = conditionCodes, opSize = ""),
IsaData("jmp", "Jump", opSize = ""), IsaData(
IsaData("jsr", "Jump to Subroutine", opSize = ""), "jmp", "Jump",
IsaData("nop", "No Operation", opSize = "", hasOps = false), modes = listOf(
AllowedAdrMode(
setOf(
AddressMode.ADDRESS_REGISTER_INDIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
AddressMode.ABSOLUTE_ADDRESS,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX
), null, OP_UNSIZED
)
)
),
IsaData(
"jsr", "Jump to Subroutine",
modes = listOf(
AllowedAdrMode(
setOf(
AddressMode.ADDRESS_REGISTER_INDIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
AddressMode.ABSOLUTE_ADDRESS,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX
), null, OP_UNSIZED
)
)
),
IsaData("nop", "No Operation", hasOps = false, modes = NO_OPS_UNSIZED),
IsaData("rtr", "Return and Restore", hasOps = false), IsaData("rtr", "Return and Restore", hasOps = false, modes = NO_OPS_UNSIZED),
IsaData("rts", "Return from Subroutine", hasOps = false), IsaData("rts", "Return from Subroutine", hasOps = false, modes = NO_OPS_UNSIZED),
IsaData("tst", "Test Operand"), IsaData("tst", "Test Operand", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null))),
// System Control Instructions // System Control Instructions
IsaData("andi", "AND Immediate to Status Register", id = "andi to SR", altMnemonics = listOf("and"), isPrivileged = true), IsaData(
IsaData("eori", "Exclusive-OR Immediate to Status Register", id = "eori to SR", altMnemonics = listOf("eor"), isPrivileged = true), "andi", "AND Immediate to Status Register", id = "andi to SR", altMnemonics = listOf("and"), isPrivileged = true,
IsaData("ori", "Inclusive-OR Immediate to Status Register", id = "ori to SR", altMnemonics = listOf("or"), isPrivileged = true), modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr"))
IsaData("move", "Move from Status Register", id = "move from SR"), ),
IsaData("move", "Move to Status Register", id = "move to SR", isPrivileged = true), IsaData(
IsaData("move", "Move User Stack Pointer", id = "move USP", isPrivileged = true), "eori", "Exclusive-OR Immediate to Status Register", id = "eori to SR", altMnemonics = listOf("eor"), isPrivileged = true,
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr"))
),
IsaData(
"ori", "Inclusive-OR Immediate to Status Register", id = "ori to SR", altMnemonics = listOf("or"), isPrivileged = true,
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr"))
),
IsaData(
"move", "Move from Status Register", id = "move from SR",
modes = listOf(AllowedAdrMode(setOf(AddressMode.SPECIAL_REGISTER_DIRECT), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, OP_SIZE_W, "sr"))
),
IsaData(
"move", "Move to Status Register", id = "move to SR", isPrivileged = true,
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr"))
),
IsaData(
"move", "Move User Stack Pointer", id = "move USP", isPrivileged = true,
modes = listOf(
AllowedAdrMode(setOf(AddressMode.SPECIAL_REGISTER_DIRECT), AREG_ONLY, OP_SIZE_L, "usp"),
AllowedAdrMode(AREG_ONLY, setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_L, "usp"),
)
),
IsaData("reset", "Reset External Devices", opSize = "", isPrivileged = true, hasOps = false), IsaData("reset", "Reset External Devices", isPrivileged = true, hasOps = false, modes = NO_OPS_UNSIZED),
IsaData("rte", "Return from Exception", isPrivileged = true, hasOps = false), IsaData("rte", "Return from Exception", isPrivileged = true, hasOps = false, modes = NO_OPS_UNSIZED),
IsaData("stop", "Stop", opSize = "", isPrivileged = true), IsaData(
"stop", "Stop", isPrivileged = true,
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), null, OP_UNSIZED))
),
IsaData("chk", "Check Register Against Bound"), IsaData(
IsaData("illegal", "Take Illegal Instruction Trap", opSize = "", hasOps = false), "chk", "Check Register Against Bound",
IsaData("trap", "Trap", opSize = ""), modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W))
IsaData("trapv", "Trap on Overflow", opSize = ""), ),
IsaData("illegal", "Take Illegal Instruction Trap", hasOps = false, modes = NO_OPS_UNSIZED),
IsaData("andi", "AND Immediate to Condition Code Register", id = "andi to CCR", altMnemonics = listOf("and")), IsaData("trap", "Trap", modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), null, OP_UNSIZED))),
IsaData("eori", "Exclusive-OR Immediate to Condition Code Register", id = "eori to CCR", altMnemonics = listOf("eor")), IsaData("trapv", "Trap on Overflow", hasOps = false, modes = NO_OPS_UNSIZED),
IsaData("ori", "Inclusive-OR Immediate to Condition Code Register", id = "ori to CCR", altMnemonics = listOf("or")),
IsaData("move", "Move to Condition Code Register", id = "move to CCR"),
IsaData(
"andi", "AND Immediate to Condition Code Register", id = "andi to CCR", altMnemonics = listOf("and"),
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "ccr"))
),
IsaData(
"eori", "Exclusive-OR Immediate to Condition Code Register", id = "eori to CCR", altMnemonics = listOf("eor"),
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "ccr"))
),
IsaData(
"ori", "Inclusive-OR Immediate to Condition Code Register", id = "ori to CCR", altMnemonics = listOf("or"),
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "ccr"))
),
IsaData(
"move", "Move to Condition Code Register", id = "move to CCR",
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "ccr"))
),
// Multiprocessor Instructions // Multiprocessor Instructions
IsaData("tas", "Test Operand and Set", opSize = "B"), IsaData("tas", "Test Operand and Set", modes = listOf(AllowedAdrMode(ALL_EXCEPT_IMMEDIATE_AND_PC_REL, null, OP_SIZE_B)))
)
)
val mnemonics = val mnemonics =
isaData.asSequence() isaData.asSequence()
@ -161,4 +555,47 @@ object M68kIsa {
}) })
} }
.toSet() .toSet()
fun findMatchingInstruction(mnemonic: String): List<IsaData> {
val lowerMnemonic = mnemonic.lowercase()
return isaData
.filter {
if (it.conditionCodes.isEmpty()) {
(it.mnemonic == lowerMnemonic) || it.altMnemonics.any { altMnemonic -> altMnemonic == lowerMnemonic }
} else {
it.altMnemonics.any { altMnemonic -> altMnemonic == lowerMnemonic } ||
it.conditionCodes.any { cc ->
it.mnemonic.replace("CC", cc) == lowerMnemonic
}
}
}
}
fun findMatchingOpMode(candidates: List<IsaData>, op1: AddressMode?, op2: AddressMode?, opSize: Int?, specialReg: String?): List<IsaData> {
return candidates.filter {
it.modes.any { am ->
isAddressModeMatching(am, op1, op2, specialReg)
&& ((opSize == null) || ((opSize and am.size) == opSize))
}
}
}
fun findMatchingOpModeIgnoringSize(candidates: List<IsaData>, op1: AddressMode?, op2: AddressMode?, specialReg: String?): List<IsaData> {
return candidates.filter {
it.modes.any { am -> isAddressModeMatching(am, op1, op2, specialReg) }
}
}
fun findSupportedOpSizes(candidates: List<IsaData>, op1: AddressMode?, op2: AddressMode?, specialReg: String?): Int {
return candidates.fold(OP_UNSIZED) { acc, isaData ->
acc or isaData.modes.fold(OP_UNSIZED) { am_acc, am ->
if (isAddressModeMatching(am, op1, op2, specialReg)) am_acc or am.size else am_acc
}
}
}
private fun isAddressModeMatching(am: AllowedAdrMode, op1: AddressMode?, op2: AddressMode?, specialReg: String?) =
((((op1 == null) && (am.op1 == null)) || am.op1?.contains(op1) ?: false)
&& (((op2 == null) && (am.op2 == null)) || am.op2?.contains(op2) ?: false)
&& ((specialReg == null) || (specialReg == am.specialReg)))
} }

View File

@ -0,0 +1,44 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.codeInspection.InspectionManager
import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor
import de.platon42.intellij.plugins.m68k.psi.*
abstract class AbstractBaseM68kLocalInspectionTool : LocalInspectionTool() {
protected open fun checkStatement(statement: M68kStatement, manager: InspectionManager, isOnTheFly: Boolean): Array<ProblemDescriptor>? = null
protected open fun checkAsmInstruction(asmInstruction: M68kAsmInstruction, manager: InspectionManager, isOnTheFly: Boolean): Array<ProblemDescriptor>? =
null
protected open fun checkMacroCall(macroCall: M68kMacroCall, manager: InspectionManager, isOnTheFly: Boolean): Array<ProblemDescriptor>? = null
protected open fun checkDirective(directive: M68kPreprocessorDirective, manager: InspectionManager, isOnTheFly: Boolean): Array<ProblemDescriptor>? = null
protected open fun checkRegister(register: M68kRegister, manager: InspectionManager, isOnTheFly: Boolean): Array<ProblemDescriptor>? = null
override fun getGroupDisplayName() = "M68k"
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : M68kVisitor() {
override fun visitStatement(statement: M68kStatement) = addDescriptors(checkStatement(statement, holder.manager, isOnTheFly))
override fun visitAsmInstruction(asmInstruction: M68kAsmInstruction) =
addDescriptors(checkAsmInstruction(asmInstruction, holder.manager, isOnTheFly))
override fun visitMacroCall(macroCall: M68kMacroCall) = addDescriptors(checkMacroCall(macroCall, holder.manager, isOnTheFly))
override fun visitPreprocessorDirective(directive: M68kPreprocessorDirective) =
addDescriptors(checkDirective(directive, holder.manager, isOnTheFly))
override fun visitRegister(register: M68kRegister) = addDescriptors(checkRegister(register, holder.manager, isOnTheFly))
private fun addDescriptors(descriptors: Array<ProblemDescriptor>?) {
descriptors?.forEach(holder::registerProblem)
}
}
}
}

View File

@ -0,0 +1,193 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.codeInspection.InspectionManager
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import de.platon42.intellij.plugins.m68k.asm.*
import de.platon42.intellij.plugins.m68k.asm.M68kIsa.findMatchingInstruction
import de.platon42.intellij.plugins.m68k.asm.M68kIsa.findMatchingOpMode
import de.platon42.intellij.plugins.m68k.asm.M68kIsa.findMatchingOpModeIgnoringSize
import de.platon42.intellij.plugins.m68k.asm.M68kIsa.findSupportedOpSizes
import de.platon42.intellij.plugins.m68k.psi.M68kAddressModeUtil.getAddressModeForType
import de.platon42.intellij.plugins.m68k.psi.M68kAsmInstruction
import de.platon42.intellij.plugins.m68k.psi.M68kSpecialRegisterDirectAddressingMode
class M68kSyntaxInspection : AbstractBaseM68kLocalInspectionTool() {
companion object {
private const val DISPLAY_NAME = "Assembly instruction validity"
private const val INSTRUCTION_NOT_FOUND_MSG = "Instruction '#ref' not supported on selected cpu"
private const val OPERANDS_UNEXPECTED_MSG_TEMPLATE = "No operands expected for '%s'"
private const val SECOND_OP_UNEXPECTED_MSG_TEMPLATE = "Second operand '#ref' unexpected for '%s'"
private const val UNSUPPORTED_ADDRESSING_MODE_MSG_TEMPLATE = "Unsupported addressing mode for '%s'"
private const val UNSUPPORTED_ADDRESSING_MODE_OP1_MSG_TEMPLATE = "Unsupported addressing mode '#ref' for first operand of '%s'"
private const val UNSUPPORTED_ADDRESSING_MODE_OP2_MSG_TEMPLATE = "Unsupported addressing mode '#ref' for second operand of '%s'"
private const val UNSUPPORTED_ADDRESSING_MODE_FLIP_MSG_TEMPLATE = "Unsupported addressing modes for operands in this order for '%s'"
private const val UNSUPPORTED_SIZE_UNSIZED_MSG_TEMPLATE = "Instruction '%s' is unsized"
private const val UNSUPPORTED_SIZE_MSG_TEMPLATE = "Operation size '#ref' unsupported for '%s"
private const val UNSUPPORTED_SIZE_BYTE_MSG = "Operation size '#ref' unsupported (should be .b)"
private const val UNSUPPORTED_SIZE_WORD_MSG = "Operation size '#ref' unsupported (should be .w)"
private const val UNSUPPORTED_SIZE_LONG_MSG = "Operation size '#ref' unsupported (should be .l)"
private const val UNSUPPORTED_SIZE_WORD_OR_LONG_MSG = "Operation size '#ref' unsupported (should be .w or .l)"
}
override fun getDisplayName() = DISPLAY_NAME
override fun checkAsmInstruction(asmInstruction: M68kAsmInstruction, manager: InspectionManager, isOnTheFly: Boolean): Array<ProblemDescriptor>? {
val asmOp = asmInstruction.asmOp
val mnemonicWithSize = asmOp.text
val isaData = findMatchingInstruction(asmOp.mnemonic)
if (isaData.isEmpty()) {
return arrayOf(
manager.createProblemDescriptor(
asmOp,
INSTRUCTION_NOT_FOUND_MSG,
null as LocalQuickFix?,
ProblemHighlightType.ERROR,
isOnTheFly
)
)
}
if (asmInstruction.addressingModeList.isNotEmpty() && isaData.none { it.hasOps }) {
return arrayOf(
manager.createProblemDescriptor(
asmInstruction.addressingModeList.first(),
asmInstruction.addressingModeList.last(),
OPERANDS_UNEXPECTED_MSG_TEMPLATE.format(mnemonicWithSize),
ProblemHighlightType.ERROR,
isOnTheFly
)
)
}
val op1 = getAddressModeForType(asmInstruction.addressingModeList.getOrNull(0))
val op2 = getAddressModeForType(asmInstruction.addressingModeList.getOrNull(1))
val specialReg1 = (asmInstruction.addressingModeList.getOrNull(0) as? M68kSpecialRegisterDirectAddressingMode)?.specialRegister?.text
val specialReg2 = (asmInstruction.addressingModeList.getOrNull(1) as? M68kSpecialRegisterDirectAddressingMode)?.specialRegister?.text
val opSize = asmOp.opSize
val matchingModeIsaData = findMatchingOpMode(isaData, op1, op2, opSize, specialReg1 ?: specialReg2)
if (matchingModeIsaData.isEmpty()) {
val matchingModeIsaDataIgnoringSize = findMatchingOpModeIgnoringSize(isaData, op1, op2, specialReg1 ?: specialReg2)
if (matchingModeIsaDataIgnoringSize.isEmpty()) {
val matchingModeIsaDataSwapped = findMatchingOpModeIgnoringSize(isaData, op2, op1, specialReg1 ?: specialReg2)
val supportedModesOp1 = isaData.flatMap { it.modes.flatMap { am -> am.op1 ?: emptySet() } }.toSet()
val supportedModesOp2 = isaData.flatMap { it.modes.flatMap { am -> am.op2 ?: emptySet() } }.toSet()
if (matchingModeIsaDataSwapped.isNotEmpty()) {
return arrayOf(
manager.createProblemDescriptor(
asmInstruction,
UNSUPPORTED_ADDRESSING_MODE_FLIP_MSG_TEMPLATE.format(mnemonicWithSize),
null as LocalQuickFix?, // TODO add flipping quickfix
ProblemHighlightType.ERROR,
isOnTheFly
)
)
}
if ((op2 != null)) {
if (supportedModesOp2.isEmpty()) {
return arrayOf(
manager.createProblemDescriptor(
asmInstruction.addressingModeList[1],
SECOND_OP_UNEXPECTED_MSG_TEMPLATE.format(mnemonicWithSize),
null as LocalQuickFix?,
ProblemHighlightType.ERROR,
isOnTheFly
)
)
}
if (supportedModesOp1.contains(op1) && !supportedModesOp2.contains(op2)) {
return arrayOf(
manager.createProblemDescriptor(
asmInstruction.addressingModeList[1],
UNSUPPORTED_ADDRESSING_MODE_OP2_MSG_TEMPLATE.format(mnemonicWithSize),
null as LocalQuickFix?,
ProblemHighlightType.ERROR,
isOnTheFly
)
)
}
if (supportedModesOp2.contains(op2) && !supportedModesOp1.contains(op1)) {
return arrayOf(
manager.createProblemDescriptor(
asmInstruction.addressingModeList[0],
UNSUPPORTED_ADDRESSING_MODE_OP1_MSG_TEMPLATE.format(mnemonicWithSize),
null as LocalQuickFix?,
ProblemHighlightType.ERROR,
isOnTheFly
)
)
}
}
return arrayOf(
manager.createProblemDescriptor(
asmInstruction.addressingModeList.firstOrNull() ?: asmInstruction,
asmInstruction.addressingModeList.lastOrNull() ?: asmInstruction,
UNSUPPORTED_ADDRESSING_MODE_MSG_TEMPLATE.format(mnemonicWithSize),
ProblemHighlightType.ERROR,
isOnTheFly
)
)
}
val supportedOpSizes = findSupportedOpSizes(matchingModeIsaDataIgnoringSize, op1, op2, specialReg1 ?: specialReg2)
return arrayOf(
when (supportedOpSizes) {
OP_UNSIZED ->
manager.createProblemDescriptor(
asmOp.operandSize ?: asmOp,
UNSUPPORTED_SIZE_UNSIZED_MSG_TEMPLATE.format(asmOp.mnemonic),
null as LocalQuickFix?, // TODO remove size quickfix?
ProblemHighlightType.ERROR,
isOnTheFly
)
OP_SIZE_B ->
manager.createProblemDescriptor(
asmOp.operandSize ?: asmOp,
UNSUPPORTED_SIZE_BYTE_MSG,
null as LocalQuickFix?, // TODO change size to .b?
ProblemHighlightType.ERROR,
isOnTheFly
)
OP_SIZE_W ->
manager.createProblemDescriptor(
asmOp.operandSize ?: asmOp,
UNSUPPORTED_SIZE_WORD_MSG,
null as LocalQuickFix?, // TODO change size to .w?
ProblemHighlightType.ERROR,
isOnTheFly
)
OP_SIZE_L ->
manager.createProblemDescriptor(
asmOp.operandSize ?: asmOp,
UNSUPPORTED_SIZE_LONG_MSG,
null as LocalQuickFix?, // TODO change size to .l?
ProblemHighlightType.ERROR,
isOnTheFly
)
OP_SIZE_WL ->
manager.createProblemDescriptor(
asmOp.operandSize ?: asmOp,
UNSUPPORTED_SIZE_WORD_OR_LONG_MSG,
null as LocalQuickFix?,
ProblemHighlightType.ERROR,
isOnTheFly
)
else ->
manager.createProblemDescriptor(
asmOp.operandSize ?: asmOp,
UNSUPPORTED_SIZE_MSG_TEMPLATE.format(asmOp.mnemonic),
null as LocalQuickFix?, // TODO remove size quickfix?
ProblemHighlightType.ERROR,
isOnTheFly
)
}
)
}
return emptyArray()
}
}

View File

@ -161,11 +161,17 @@ GlobalLabel ::= GLOBAL_LABEL_DEF COLON* {
private Label ::= LocalLabel | GlobalLabel private Label ::= LocalLabel | GlobalLabel
OperandSize ::= (OPSIZE_BS|OPSIZE_W|OPSIZE_L) { name = ".s|.b|.w|.l" } OperandSize ::= (OPSIZE_BS|OPSIZE_W|OPSIZE_L) {
name = ".s|.b|.w|.l"
methods = [getSize]
}
AddressSize ::= (OPSIZE_W|OPSIZE_L) { name = ".w|.l" } AddressSize ::= (OPSIZE_W|OPSIZE_L) { name = ".w|.l" }
DataWidth ::= (OPSIZE_W|OPSIZE_L) { name = ".w|.l" } DataWidth ::= (OPSIZE_W|OPSIZE_L) { name = ".w|.l" }
AsmOp ::= MNEMONIC OperandSize? AsmOp ::= MNEMONIC OperandSize? {
name = "mnemonic"
methods = [getMnemonic getOpSize]
}
PreprocessorDirective ::= Label? (DATA_DIRECTIVE | OTHER_DIRECTIVE) PreprocessorDirective ::= Label? (DATA_DIRECTIVE | OTHER_DIRECTIVE)
PreprocessorOperands? PreprocessorOperands?
@ -174,7 +180,7 @@ MacroPlainLine ::= MACRO_LINE
MacroNameDefinition ::= MACRO_NAME MacroNameDefinition ::= MACRO_NAME
MacroDefinition ::= ((MacroNameDefinition MACRO_TAG)|(MACRO_TAG MacroNameDefinition)) MacroPlainLine* MACRO_END_TAG { MacroDefinition ::= ((MacroNameDefinition MACRO_TAG)|(MACRO_TAG MacroNameDefinition)) MacroPlainLine* MACRO_END_TAG {
pin=1 pin = 1
name = "macro definition" name = "macro definition"
implements = "de.platon42.intellij.plugins.m68k.psi.M68kNamedElement" implements = "de.platon42.intellij.plugins.m68k.psi.M68kNamedElement"
mixin = "de.platon42.intellij.plugins.m68k.psi.M68kMacroDefinitionMixin" mixin = "de.platon42.intellij.plugins.m68k.psi.M68kMacroDefinitionMixin"

View File

@ -0,0 +1,29 @@
package de.platon42.intellij.plugins.m68k.psi
import de.platon42.intellij.plugins.m68k.asm.AddressMode
object M68kAddressModeUtil {
fun getAddressModeForType(addressingMode: M68kAddressingMode?): AddressMode? {
if (addressingMode == null) return null
return when (addressingMode) {
is M68kImmediateData -> AddressMode.IMMEDIATE_DATA
is M68kAddressRegisterIndirectAddressingMode -> AddressMode.ADDRESS_REGISTER_INDIRECT
is M68kAddressRegisterIndirectPostIncAddressingMode -> AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC
is M68kAddressRegisterIndirectPreDecAddressingMode -> AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC
is M68kAddressRegisterIndirectWithDisplacementNewAddressingMode -> AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT
is M68kAddressRegisterIndirectWithDisplacementOldAddressingMode -> AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT
is M68kAddressRegisterIndirectWithIndexNewAddressingMode -> AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX
is M68kAddressRegisterIndirectWithIndexOldAddressingMode -> AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX
is M68kProgramCounterIndirectWithDisplacementNewAddressingMode -> AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT
is M68kProgramCounterIndirectWithDisplacementOldAddressingMode -> AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT
is M68kProgramCounterIndirectWithIndexNewAddressingMode -> AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX
is M68kProgramCounterIndirectWithIndexOldAddressingMode -> AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX
is M68kSpecialRegisterDirectAddressingMode -> AddressMode.SPECIAL_REGISTER_DIRECT
is M68kDataRegisterDirectAddressingMode -> AddressMode.DATA_REGISTER_DIRECT
is M68kAddressRegisterDirectAddressingMode -> AddressMode.ADDRESS_REGISTER_DIRECT
is M68kRegisterListAddressingMode -> AddressMode.REGISTER_LIST
is M68kAbsoluteAddressAddressingMode -> AddressMode.ABSOLUTE_ADDRESS
else -> throw IllegalArgumentException("Unknown addressing mode $addressingMode")
}
}
}

View File

@ -2,6 +2,7 @@ package de.platon42.intellij.plugins.m68k.psi
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.util.IncorrectOperationException import com.intellij.util.IncorrectOperationException
import de.platon42.intellij.plugins.m68k.asm.*
import de.platon42.intellij.plugins.m68k.psi.M68kElementFactory.createGlobalLabel import de.platon42.intellij.plugins.m68k.psi.M68kElementFactory.createGlobalLabel
import de.platon42.intellij.plugins.m68k.psi.M68kElementFactory.createLocalLabel import de.platon42.intellij.plugins.m68k.psi.M68kElementFactory.createLocalLabel
import de.platon42.intellij.plugins.m68k.psi.M68kElementFactory.createMacroDefinition import de.platon42.intellij.plugins.m68k.psi.M68kElementFactory.createMacroDefinition
@ -94,4 +95,22 @@ object M68kPsiImplUtil {
@JvmStatic @JvmStatic
fun getMacroName(element: M68kMacroCall): String = element.firstChild.text fun getMacroName(element: M68kMacroCall): String = element.firstChild.text
} // AsmOp
@JvmStatic
fun getMnemonic(element: M68kAsmOp): String = element.firstChild.text
@JvmStatic
fun getOpSize(element: M68kAsmOp): Int = element.operandSize?.size ?: OP_UNSIZED
// OperandSize
@JvmStatic
fun getSize(element: M68kOperandSize): Int =
when (element.text) {
null -> OP_UNSIZED
".w" -> OP_SIZE_W
".l" -> OP_SIZE_L
".b" -> OP_SIZE_B
".s" -> OP_SIZE_S
else -> throw IllegalArgumentException("Unknown op size ${element.text}")
}
}

View File

@ -40,6 +40,9 @@
<psi.referenceContributor implementation="de.platon42.intellij.plugins.m68k.refs.M68kReferenceContributor"/> <psi.referenceContributor implementation="de.platon42.intellij.plugins.m68k.refs.M68kReferenceContributor"/>
<gotoSymbolContributor implementation="de.platon42.intellij.plugins.m68k.refs.M68kChooseByNameContributor"/> <gotoSymbolContributor implementation="de.platon42.intellij.plugins.m68k.refs.M68kChooseByNameContributor"/>
<renameInputValidator implementation="de.platon42.intellij.plugins.m68k.psi.M68kRenameInputValidator"/> <renameInputValidator implementation="de.platon42.intellij.plugins.m68k.psi.M68kRenameInputValidator"/>
<localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kSyntaxInspection"
enabledByDefault="true" level="ERROR"/>
</extensions> </extensions>
<actions> <actions>

View File

@ -0,0 +1,14 @@
<html>
<body>
Reports wrong use of assembler instruction syntax.
The parser allows more combinations than supported by the target CPU.
This inspection does a strict verification on the allowed addressing modes
and operation size for each assembly mnemonic.
<!-- tooltip end -->
<p>Several different error messages may be the result of this verification,
giving hints whether the first or the second addressing mode is unsupported,
or if the operands should be swapped to make it a legal instruction.
</p>
</body>
</html>

View File

@ -0,0 +1,18 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.LightCodeInsightExtension
import de.platon42.intellij.jupiter.TestDataPath
import de.platon42.intellij.plugins.m68k.AbstractM68kTest
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Condition
import org.junit.jupiter.api.extension.ExtendWith
@ExtendWith(LightCodeInsightExtension::class)
@TestDataPath("src/test/resources/inspections")
abstract class AbstractInspectionTest : AbstractM68kTest() {
protected fun assertHighlightings(myFixture: CodeInsightTestFixture, count: Int, snippet: String) {
assertThat(myFixture.doHighlighting())
.areExactly(count, Condition({ it.description?.contains(snippet) ?: false }, "containing"))
}
}

View File

@ -0,0 +1,99 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.MyFixture
import org.junit.jupiter.api.Test
internal class M68kSyntaxInspectionTest : AbstractInspectionTest() {
@Test
internal fun shows_error_on_instruction_with_unexpected_operands(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " rts d0")
assertHighlightings(myFixture, 1, "No operands expected for 'rts'")
}
@Test
internal fun shows_error_on_instruction_with_unexpected_second_operand(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " jmp label,d0")
assertHighlightings(myFixture, 1, "Second operand 'd0' unexpected")
}
@Test
internal fun shows_unsupported_addressing_mode_in_first_operand(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " moveq.l 123,d0")
assertHighlightings(myFixture, 1, "Unsupported addressing mode '123' for first operand")
}
@Test
internal fun shows_unsupported_addressing_mode_in_second_operand(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " moveq.l #123,a0")
assertHighlightings(myFixture, 1, "Unsupported addressing mode 'a0' for second operand")
}
@Test
internal fun shows_unsupported_swapped_addressing_mode(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " cmp.l d0,(a0)")
assertHighlightings(myFixture, 1, "Unsupported addressing modes for operands in this order for 'cmp.l'")
}
@Test
internal fun shows_error_on_unsized_instruction_with_size(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " rts.l")
assertHighlightings(myFixture, 1, "Instruction 'rts' is unsized")
}
@Test
internal fun shows_error_on_unsupported_op_size_for_b(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " btst.l #5,(a0)")
assertHighlightings(myFixture, 1, "Operation size '.l' unsupported (should be .b)")
}
@Test
internal fun shows_error_on_unsupported_op_size_for_w(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " divu.b #5,d0")
assertHighlightings(myFixture, 1, "Operation size '.b' unsupported (should be .w)")
}
@Test
internal fun shows_error_on_unsupported_op_size_for_l(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " moveq.w #5,d0")
assertHighlightings(myFixture, 1, "Operation size '.w' unsupported (should be .l)")
}
@Test
internal fun shows_error_on_unsupported_op_size_for_w_or_l(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " addq.b #5,a0")
assertHighlightings(myFixture, 1, "Operation size '.b' unsupported (should be .w or .l)")
}
@Test
internal fun shows_error_on_unsupported_op_size_for_other(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " bsr.l label")
assertHighlightings(myFixture, 1, "Operation size '.l' unsupported")
}
@Test
internal fun shows_error_wrong_mode_on_instruction_with_special_reg(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " move.l usp,d0")
assertHighlightings(myFixture, 1, "Unsupported addressing mode for 'move.l'")
}
@Test
internal fun shows_error_wrong_register_on_instruction_with_special_reg(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " move.l vbr,a0")
assertHighlightings(myFixture, 1, "Unsupported addressing mode for 'move.l'")
}
}