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)
- 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)
- 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 definition refactoring and find usages support.
- New: Macro definition refactoring and find usages support.
- Enhancement: Structural View also shows macro definitions.
- Bugfix: Missing REPT and ENDR assembler directives added.
- Cosmetics: Changed or added some icons at various places.

View File

@ -7,7 +7,7 @@ plugins {
}
group = 'de.platon42'
version = '0.3'
version = '0.4'
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
@ -60,12 +60,13 @@ patchPluginXml {
<h4>V0.4 (unreleased)</h4>
<ul>
<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>
<h4>V0.3 (28-Jul-21)</h4>
<ul>
<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 definition refactoring and find usages support.
<li>New: Macro definition refactoring and find usages support.
<li>Enhancement: Structural View also shows macro definitions.
<li>Bugfix: Missing REPT and ENDR assembler directives added.
<li>Cosmetics: Changed or added some icons at various places.

View File

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

View File

@ -1,6 +1,7 @@
// This is a generated file. Not intended for manual editing.
package de.platon42.intellij.plugins.m68k.psi;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface M68kAsmOp extends M68kPsiElement {
@ -8,4 +9,9 @@ public interface M68kAsmOp extends M68kPsiElement {
@Nullable
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 {
int getSize();
}

View File

@ -7,6 +7,7 @@ import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.util.PsiTreeUtil;
import de.platon42.intellij.plugins.m68k.psi.M68kAsmOp;
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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -33,4 +34,15 @@ public class M68kAsmOpImpl extends ASTWrapperPsiElement implements M68kAsmOp {
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.psi.PsiElementVisitor;
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 org.jetbrains.annotations.NotNull;
@ -24,4 +25,9 @@ public class M68kOperandSizeImpl extends ASTWrapperPsiElement implements M68kOpe
else super.accept(visitor);
}
@Override
public int getSize() {
return M68kPsiImplUtil.getSize(this);
}
}

View File

@ -9,6 +9,39 @@ enum class Machine {
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(
val mnemonic: String,
val description: String,
@ -16,141 +49,502 @@ data class IsaData(
val altMnemonics: List<String> = emptyList(),
val conditionCodes: List<String> = emptyList(),
val id: String = mnemonic,
val opSize: String = "bWl",
val isPrivileged: Boolean = false,
val hasOps: Boolean = true
val hasOps: Boolean = true,
val modes: List<AllowedAdrMode> = listOf(AllowedAdrMode())
)
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
IsaData("move", "Move"),
IsaData("movea", "Move Address", altMnemonics = listOf("move"), opSize = "L"),
IsaData("movem", "Move Multiple Registers", opSize = "Wl"),
IsaData("movep", "Move Peripheral", opSize = ""),
IsaData("moveq", "Move Quick", opSize = "L"),
IsaData(
"move", "Move",
modes = listOf(AllowedAdrMode(ALL_68000_MODES, ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL))
),
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("lea", "Load Effective Address", opSize = ""),
IsaData("pea", "Push Effective Address", opSize = ""),
IsaData("link", "Link and Allocate", opSize = ""),
IsaData("unlk", "Unlink", opSize = ""),
IsaData(
"moveq", "Move Quick",
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), DREG_ONLY, OP_SIZE_L))
),
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
IsaData("add", "Add"),
IsaData("adda", "Add Address", altMnemonics = listOf("add"), opSize = "Wl"),
IsaData("addi", "Add Immediate", altMnemonics = listOf("add")),
IsaData("addq", "Add Quick"),
IsaData("addx", "Add with Extend"),
IsaData("add", "Add", modes = ADD_SUB_MODES),
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"),
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("suba", "Subtract Address", altMnemonics = listOf("sub")),
IsaData("subi", "Subtract Immediate", altMnemonics = listOf("sub")),
IsaData("subq", "Subtract Quick"),
IsaData("subx", "Subtract with Extend"),
IsaData("sub", "Subtract", modes = ADD_SUB_MODES),
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"),
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("negx", "Negate with Extend"),
IsaData("neg", "Negate", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null))),
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("cmpa", "Compare Address", altMnemonics = listOf("cmp"), opSize = "Wl"),
IsaData("cmpi", "Compare Immediate", altMnemonics = listOf("cmp")),
IsaData("cmpm", "Compare Memory to Memory", altMnemonics = listOf("cmp")),
IsaData(
"cmp", "Compare", 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),
)
),
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("mulu", "Unsigned Multiply", opSize = "W"),
IsaData("divs", "Signed Divide", opSize = "W"),
IsaData("divu", "Unsigned Divide", opSize = "W"),
IsaData("muls", "Signed Multiply", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W))),
IsaData("mulu", "Unsigned Multiply", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W))),
IsaData("divs", "Signed Divide", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_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
IsaData("and", "Logical AND"),
IsaData("andi", "Logical AND Immediate", altMnemonics = listOf("and")),
IsaData("eor", "Logical Exclusive-OR"),
IsaData("eori", "Logical Exclusive-OR Immediate", altMnemonics = listOf("eor")),
IsaData("not", "Logical Complement"),
IsaData("or", "Logical Inclusive-OR"),
IsaData("ori", "Logical Inclusive-OR Immediate", altMnemonics = listOf("or")),
IsaData(
"and", "Logical AND",
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY), AllowedAdrMode(DREG_ONLY, INDIRECT_MODES))
),
IsaData(
"andi", "Logical AND Immediate", altMnemonics = listOf("and"),
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
IsaData("asl", "Arithmetic Shift Left"),
IsaData("asr", "Arithmetic Shift Right"),
IsaData("lsl", "Logical Shift Left"),
IsaData("lsr", "Logical Shift Right"),
IsaData("rol", "Rotate Left"),
IsaData("ror", "Rotate Right"),
IsaData("roxl", "Rotate with Extend Left"),
IsaData("roxr", "Rotate with Extend Right"),
IsaData("swap", "Swap Register Words", opSize = ""),
IsaData("asl", "Arithmetic Shift Left", modes = ASD_LSD_ROD_ROXD_MODES),
IsaData("asr", "Arithmetic Shift Right", modes = ASD_LSD_ROD_ROXD_MODES),
IsaData("lsl", "Logical Shift Left", modes = ASD_LSD_ROD_ROXD_MODES),
IsaData("lsr", "Logical Shift Right", modes = ASD_LSD_ROD_ROXD_MODES),
IsaData("rol", "Rotate Left", modes = ASD_LSD_ROD_ROXD_MODES),
IsaData("ror", "Rotate Right", modes = ASD_LSD_ROD_ROXD_MODES),
IsaData("roxl", "Rotate with Extend Left", modes = ASD_LSD_ROD_ROXD_MODES),
IsaData("roxr", "Rotate with Extend Right", modes = ASD_LSD_ROD_ROXD_MODES),
IsaData("swap", "Swap Register Words", modes = listOf(AllowedAdrMode(DREG_ONLY, null, OP_SIZE_W))),
// Bit Manipulation Instructions
IsaData("bchg", "Test Bit and Change", opSize = "Bl"),
IsaData("bclr", "Test Bit and Clear", opSize = "Bl"),
IsaData("bset", "Test Bit and Set", opSize = "Bl"),
IsaData("btst", "Test Bit", opSize = "Bl"),
IsaData("bchg", "Test Bit and Change", modes = BTST_BCHG_BCLR_BSET_MODES),
IsaData("bclr", "Test Bit and Clear", modes = BTST_BCHG_BCLR_BSET_MODES),
IsaData("bset", "Test Bit and Set", modes = BTST_BCHG_BCLR_BSET_MODES),
IsaData("btst", "Test Bit", modes = BTST_BCHG_BCLR_BSET_MODES),
// Binary-Coded Decimal Instructions
IsaData("abcd", "Add Decimal with Extend", opSize = ""),
IsaData("sbcd", "Subtract Decimal with Extend", opSize = ""),
IsaData("nbcd", "Negate Decimal with Extend", opSize = ""),
IsaData(
"abcd", "Add 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(
"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
IsaData("bCC", "Branch Conditionally", conditionCodes = conditionCodesBcc, opSize = "bsW"),
IsaData("bra", "Branch", opSize = "bsW"),
IsaData("bsr", "Branch to Subroutine", opSize = "bsW"),
IsaData(
"bCC", "Branch Conditionally", conditionCodes = conditionCodesBcc,
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(
"dbCC",
"Test Condition, Decrement, and Branch",
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("jsr", "Jump to Subroutine", opSize = ""),
IsaData("nop", "No Operation", opSize = "", hasOps = false),
IsaData(
"jmp", "Jump",
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("rts", "Return from Subroutine", hasOps = false),
IsaData("rtr", "Return and Restore", hasOps = false, modes = NO_OPS_UNSIZED),
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
IsaData("andi", "AND Immediate to Status Register", id = "andi to SR", altMnemonics = listOf("and"), isPrivileged = true),
IsaData("eori", "Exclusive-OR Immediate to Status Register", id = "eori to SR", altMnemonics = listOf("eor"), isPrivileged = true),
IsaData("ori", "Inclusive-OR Immediate to Status Register", id = "ori to SR", altMnemonics = listOf("or"), isPrivileged = true),
IsaData("move", "Move from Status Register", id = "move from SR"),
IsaData("move", "Move to Status Register", id = "move to SR", isPrivileged = true),
IsaData("move", "Move User Stack Pointer", id = "move USP", isPrivileged = true),
IsaData(
"andi", "AND Immediate to Status Register", id = "andi to SR", altMnemonics = listOf("and"), isPrivileged = true,
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr"))
),
IsaData(
"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("rte", "Return from Exception", isPrivileged = true, hasOps = false),
IsaData("stop", "Stop", opSize = "", isPrivileged = true),
IsaData("reset", "Reset External Devices", isPrivileged = true, hasOps = false, modes = NO_OPS_UNSIZED),
IsaData("rte", "Return from Exception", isPrivileged = true, hasOps = false, modes = NO_OPS_UNSIZED),
IsaData(
"stop", "Stop", isPrivileged = true,
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), null, OP_UNSIZED))
),
IsaData("chk", "Check Register Against Bound"),
IsaData("illegal", "Take Illegal Instruction Trap", opSize = "", hasOps = false),
IsaData("trap", "Trap", opSize = ""),
IsaData("trapv", "Trap on Overflow", opSize = ""),
IsaData("andi", "AND Immediate to Condition Code Register", id = "andi to CCR", altMnemonics = listOf("and")),
IsaData("eori", "Exclusive-OR Immediate to Condition Code Register", id = "eori to CCR", altMnemonics = listOf("eor")),
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(
"chk", "Check Register Against Bound",
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W))
),
IsaData("illegal", "Take Illegal Instruction Trap", hasOps = false, modes = NO_OPS_UNSIZED),
IsaData("trap", "Trap", modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), null, OP_UNSIZED))),
IsaData("trapv", "Trap on Overflow", hasOps = false, modes = NO_OPS_UNSIZED),
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
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 =
isaData.asSequence()
@ -161,4 +555,47 @@ object M68kIsa {
})
}
.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
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" }
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)
PreprocessorOperands?
@ -174,7 +180,7 @@ MacroPlainLine ::= MACRO_LINE
MacroNameDefinition ::= MACRO_NAME
MacroDefinition ::= ((MacroNameDefinition MACRO_TAG)|(MACRO_TAG MacroNameDefinition)) MacroPlainLine* MACRO_END_TAG {
pin=1
pin = 1
name = "macro definition"
implements = "de.platon42.intellij.plugins.m68k.psi.M68kNamedElement"
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.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.createLocalLabel
import de.platon42.intellij.plugins.m68k.psi.M68kElementFactory.createMacroDefinition
@ -94,4 +95,22 @@ object M68kPsiImplUtil {
@JvmStatic
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"/>
<gotoSymbolContributor implementation="de.platon42.intellij.plugins.m68k.refs.M68kChooseByNameContributor"/>
<renameInputValidator implementation="de.platon42.intellij.plugins.m68k.psi.M68kRenameInputValidator"/>
<localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kSyntaxInspection"
enabledByDefault="true" level="ERROR"/>
</extensions>
<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'")
}
}