diff --git a/README.md b/README.md
index 1e54b1a..5abc4b9 100644
--- a/README.md
+++ b/README.md
@@ -19,12 +19,13 @@ awesome features and is pretty advanced. Check it out. You can install both plug
Big kudos to Yann -- a few features were _inspired_ by his code.
-My plugin, on the other hand, is still pretty basic and is the result of about a week's effort. I released a really early first version it because I think
+My plugin, on the other hand, is still pretty basic and is the result of about two weeks of work. I released a really early first version it because I think
it's "good enough" to get started, and I can return to demo coding with its current state.
## Features
- Parser / Lexer for MC68000 (yes, only 68000 right now!) assembly language files in VAsm / DevPac style
+- Inspection for validating the syntax the 68000 ISA.
- Syntax highlighting and Color Settings Page (you should really modify the color settings to your likings!)
- Mnemonics code completion
- Symbols / Labels / Macros code completion
@@ -33,6 +34,7 @@ it's "good enough" to get started, and I can return to demo coding with its curr
- Quote handler
- Goto Symbol support
- Structure view
+- Documentation provider for symbol definitions and mnemonics (listing available addressing modes etc.).
## Known issues
@@ -42,7 +44,6 @@ it's "good enough" to get started, and I can return to demo coding with its curr
- No support for register replacement (e.g. registers replaced by `EQUR` or `EQURL` will cause syntax errors)
- While the Lexer supports the -spaces option (where a space introduces a comment), this cannot be configured yet (default is off).
- No support for other processor instructions, FPU or 68020+ address modes.
-- For versions < V0.4: No semantic checking for allowed address modes or data widths yet.
- Unit Test coverage is not as good as it could be (ahem).
- Missing but planned features:
- Macro evaluation on invocation
@@ -70,11 +71,14 @@ make it work with JUnit 5. Feel free to use the code (in package ```de.platon42.
### V0.4 (unreleased)
+- Notice: Due to major new API use, this plugin no longer works on IDEs >=2019.3.1, but rather requires >=2020.3.
- Enhancement: Added Structure View filters.
- New: Added inspection to validate the correctness of a MC68000 instruction regarding operation size and address modes.
- Bugfix: Added several missing assembler directives (`opt`, `machine`, etc.).
- Bugfix: Uppercase hexadecimal literals were not parsed (JFlex bug?).
- Bugfix: Interpretation of register lists was wrong in BNF.
+- New: Added Documentation Provider for symbol definitions (shows assigned declaration).
+- New: Added Documentation Provider for mnemonics (simple version, generated out of ISA information).
### V0.3 (28-Jul-21)
diff --git a/build.gradle b/build.gradle
index a047980..417b648 100644
--- a/build.gradle
+++ b/build.gradle
@@ -48,10 +48,7 @@ intellij {
}
runPluginVerifier {
- ideVersions = ["IC-193.5662.53", "IC-212.4746.92", // 2019.3.1 - 2021.2
- "CL-193.5233.103", "CL-193.7288.25", // 2019.3
- "CL-201.6668.126", "CL-201.8743.17", // 2020.1
- "CL-202.6397.106", "CL-202.8194.17", // 2020.2
+ ideVersions = ["IC-203.6682.168", "IC-212.4746.92", // 2020.3 - 2021.2
"CL-203.5981.166", "CL-203.8084.11", // 2020.3
"CL-211.6693.114", "CL-211.7628.27", // 2021.1
"CL-212.4746.93"] // 2021.2
@@ -62,11 +59,14 @@ patchPluginXml {
setChangeNotes("""
V0.4 (unreleased)
+ - Notice: Due to major new API use, this plugin no longer works on IDEs >=2019.3.1, but rather requires >=2020.3.
- Enhancement: Added Structure View filters.
- New: Added inspection to validate the correctness of a MC68000 instruction regarding operation size and address modes.
- Bugfix: Added several missing assembler directives (opt, machine, etc.).
- Bugfix: Uppercase hexadecimal literals were not parsed (JFlex bug?).
- Bugfix: Interpretation of register lists was wrong in BNF.
+
- New: Added Documentation Provider for symbol definitions (shows assigned declaration).
+
- New: Added Documentation Provider for mnemonics (simple version, generated out of ISA information).
V0.3 (28-Jul-21)
diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/asm/M68kIsa.kt b/src/main/java/de/platon42/intellij/plugins/m68k/asm/M68kIsa.kt
index 5787739..44e4302 100644
--- a/src/main/java/de/platon42/intellij/plugins/m68k/asm/M68kIsa.kt
+++ b/src/main/java/de/platon42/intellij/plugins/m68k/asm/M68kIsa.kt
@@ -34,20 +34,20 @@ enum class Register(val regname: String, val num: Int) {
}
}
-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
+enum class AddressMode(val description: String, val syntax: String) {
+ DATA_REGISTER_DIRECT("data register direct", "Dn"),
+ ADDRESS_REGISTER_DIRECT("address register direct", "An"),
+ ADDRESS_REGISTER_INDIRECT("address register indirect", "(An)"),
+ ADDRESS_REGISTER_INDIRECT_POST_INC("address register indirect with postincrement", "(An)+"),
+ ADDRESS_REGISTER_INDIRECT_PRE_DEC("address register indirect with predecrement", "-(An)"),
+ ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT("address register indirect with displacement", "(d16,An)"),
+ PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT("program counter indirect with displacement", "(d16,PC)"),
+ ADDRESS_REGISTER_INDIRECT_WITH_INDEX("address register indirect with index", "(d8,An,Xn)"),
+ PROGRAM_COUNTER_INDIRECT_WITH_INDEX("program counter indirect with index", "(d8,PC,Xn)"),
+ SPECIAL_REGISTER_DIRECT("special register", "ccr|usp|vbr"),
+ REGISTER_LIST("register list", "list"),
+ IMMEDIATE_DATA("immediate", "#"),
+ ABSOLUTE_ADDRESS("absolute short/long", "(xxx).w|l")
}
const val OP_UNSIZED = 0
@@ -731,7 +731,7 @@ object M68kIsa {
}
.toSet()
- fun findMatchingInstruction(mnemonic: String): List {
+ fun findMatchingInstructions(mnemonic: String): List {
val lowerMnemonic = mnemonic.lowercase()
return isaData
.filter {
@@ -755,6 +755,13 @@ object M68kIsa {
}
}
+ fun findMatchingAddressMode(modes: List, op1: AddressMode?, op2: AddressMode?, opSize: Int?, specialReg: String?): List {
+ return modes.filter { am ->
+ isAddressModeMatching(am, op1, op2, specialReg)
+ && ((opSize == null) || ((opSize and am.size) == opSize))
+ }
+ }
+
fun findMatchingOpModeIgnoringSize(candidates: List, op1: AddressMode?, op2: AddressMode?, specialReg: String?): List {
return candidates.filter {
it.modes.any { am -> isAddressModeMatching(am, op1, op2, specialReg) }
diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/documentation/M68kInstructionDocumentationProvider.kt b/src/main/java/de/platon42/intellij/plugins/m68k/documentation/M68kInstructionDocumentationProvider.kt
new file mode 100644
index 0000000..be57905
--- /dev/null
+++ b/src/main/java/de/platon42/intellij/plugins/m68k/documentation/M68kInstructionDocumentationProvider.kt
@@ -0,0 +1,102 @@
+package de.platon42.intellij.plugins.m68k.documentation
+
+import com.intellij.lang.documentation.AbstractDocumentationProvider
+import com.intellij.lang.documentation.DocumentationMarkup
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.util.text.HtmlBuilder
+import com.intellij.openapi.util.text.HtmlChunk
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiFile
+import de.platon42.intellij.plugins.m68k.asm.AddressMode
+import de.platon42.intellij.plugins.m68k.asm.IsaData
+import de.platon42.intellij.plugins.m68k.asm.M68kIsa
+import de.platon42.intellij.plugins.m68k.psi.M68kAsmInstruction
+import de.platon42.intellij.plugins.m68k.psi.M68kAsmOp
+import de.platon42.intellij.plugins.m68k.psi.M68kOperandSize
+import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.findExactIsaDataForInstruction
+import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.findOpSizeDescriptions
+
+class M68kInstructionDocumentationProvider : AbstractDocumentationProvider() {
+
+ override fun generateDoc(element: PsiElement, originalElement: PsiElement?): String? {
+ if (element is M68kAsmInstruction) {
+ val builder = HtmlBuilder()
+ val isaData = findExactIsaDataForInstruction(element)
+ if (isaData == null) {
+ M68kIsa.findMatchingInstructions(element.asmOp.mnemonic).forEach { buildDocForIsaData(it, builder) }
+ } else {
+ buildDocForIsaData(isaData, builder)
+ }
+ return builder.toString()
+ }
+ return null
+ }
+
+ private fun buildDocForIsaData(isaData: IsaData, builder: HtmlBuilder) {
+ val defBuilder = createDefinition(isaData)
+ builder.append(defBuilder.wrapWith(DocumentationMarkup.DEFINITION_ELEMENT))
+
+ val bindingRows = HtmlBuilder()
+ val headerCells = listOf(
+ HtmlChunk.text("Mnemonic").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL),
+ HtmlChunk.text("Op1").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL),
+ HtmlChunk.text("Op2").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL)
+ )
+ bindingRows.append(HtmlChunk.tag("tr").children(headerCells))
+ isaData.modes.forEach { allowedAdrMode ->
+ val mnemonics = findOpSizeDescriptions(allowedAdrMode.size)
+ .map { HtmlChunk.text(isaData.mnemonic + it).wrapWith(HtmlChunk.p()) }
+ bindingRows.append(
+ HtmlChunk.tag("tr").children(
+ DocumentationMarkup.SECTION_CONTENT_CELL.children(mnemonics),
+ DocumentationMarkup.SECTION_CONTENT_CELL.child(collectAddressModes(allowedAdrMode.op1)),
+ DocumentationMarkup.SECTION_CONTENT_CELL.child(collectAddressModes(allowedAdrMode.op2))
+ )
+ )
+ }
+
+ val contentBuilder = HtmlBuilder()
+ contentBuilder.append(bindingRows.br().wrapWith(DocumentationMarkup.SECTIONS_TABLE))
+
+ builder.append(contentBuilder.wrapWith(DocumentationMarkup.CONTENT_ELEMENT))
+ }
+
+ override fun generateHoverDoc(element: PsiElement, originalElement: PsiElement?): String? {
+ if (element is M68kAsmInstruction) {
+ val isaData = findExactIsaDataForInstruction(element) ?: return null
+ val defBuilder = createDefinition(isaData)
+
+ val builder = HtmlBuilder()
+ builder.append(defBuilder.wrapWith(DocumentationMarkup.DEFINITION_ELEMENT))
+ return builder.toString()
+ }
+ return null
+ }
+
+ private fun createDefinition(isaData: IsaData): HtmlBuilder {
+ val defBuilder = HtmlBuilder()
+ defBuilder.append(HtmlChunk.text(isaData.description).bold().wrapWith("pre"))
+ if (isaData.isPrivileged) {
+ defBuilder.append(HtmlChunk.font("red").addText("(privileged)").wrapWith("p"))
+ }
+ return defBuilder
+ }
+
+ private fun collectAddressModes(addressModes: Set?): HtmlChunk {
+ if (addressModes == null) return HtmlChunk.text("")
+ val modes = HtmlBuilder()
+ addressModes.sortedBy(AddressMode::ordinal)
+ .forEach { modes.append(HtmlChunk.text(it.syntax).wrapWith(HtmlChunk.p())) }
+ return modes.toFragment()
+ }
+
+ override fun getCustomDocumentationElement(editor: Editor, file: PsiFile, contextElement: PsiElement?, targetOffset: Int): PsiElement? {
+ if (contextElement == null) return null
+ if (contextElement is M68kAsmInstruction) return contextElement
+ val parent = contextElement.parent ?: return null
+ if (parent is M68kAsmInstruction) return parent
+ if (parent is M68kAsmOp) return parent.parent
+ if (parent is M68kOperandSize) return parent.parent.parent
+ return null
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/documentation/M68kRegisterFlowDocumentationProvider.kt b/src/main/java/de/platon42/intellij/plugins/m68k/documentation/M68kRegisterFlowDocumentationProvider.kt
new file mode 100644
index 0000000..62a4773
--- /dev/null
+++ b/src/main/java/de/platon42/intellij/plugins/m68k/documentation/M68kRegisterFlowDocumentationProvider.kt
@@ -0,0 +1,39 @@
+package de.platon42.intellij.plugins.m68k.documentation
+
+import com.intellij.lang.documentation.AbstractDocumentationProvider
+import com.intellij.openapi.editor.Editor
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiFile
+import com.intellij.psi.util.PsiTreeUtil
+import de.platon42.intellij.plugins.m68k.asm.RWM_OP1_SHIFT
+import de.platon42.intellij.plugins.m68k.asm.RWM_OP2_SHIFT
+import de.platon42.intellij.plugins.m68k.asm.Register.Companion.getRegFromName
+import de.platon42.intellij.plugins.m68k.psi.M68kAddressRegister
+import de.platon42.intellij.plugins.m68k.psi.M68kAddressingMode
+import de.platon42.intellij.plugins.m68k.psi.M68kAsmInstruction
+import de.platon42.intellij.plugins.m68k.psi.M68kDataRegister
+import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.findExactIsaDataAndAllowedAdrModeForInstruction
+
+class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
+ override fun generateDoc(element: PsiElement, originalElement: PsiElement?): String? {
+ if (element is M68kDataRegister || element is M68kAddressRegister) {
+ val register = getRegFromName(element.text)
+ val addressingMode = PsiTreeUtil.getParentOfType(element, M68kAddressingMode::class.java) ?: return null
+ val asmInstruction = PsiTreeUtil.getParentOfType(addressingMode, M68kAsmInstruction::class.java) ?: return null
+ val (isaData, adrMode) = findExactIsaDataAndAllowedAdrModeForInstruction(asmInstruction) ?: return "unknown instruction"
+
+ val opShift = if (asmInstruction.addressingModeList[0] == addressingMode) RWM_OP1_SHIFT else RWM_OP2_SHIFT
+
+ return register.regname
+ }
+ return null
+ }
+
+ override fun getCustomDocumentationElement(editor: Editor, file: PsiFile, contextElement: PsiElement?, targetOffset: Int): PsiElement? {
+ if (contextElement == null) return null
+ if (contextElement is M68kDataRegister || contextElement is M68kAddressRegister) return contextElement
+ val parent = contextElement.parent ?: return null
+ if (parent is M68kDataRegister || parent is M68kAddressRegister) return parent
+ return null
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/documentation/M68kSymbolDefinitionDocumentationProvider.kt b/src/main/java/de/platon42/intellij/plugins/m68k/documentation/M68kSymbolDefinitionDocumentationProvider.kt
new file mode 100644
index 0000000..e03df8b
--- /dev/null
+++ b/src/main/java/de/platon42/intellij/plugins/m68k/documentation/M68kSymbolDefinitionDocumentationProvider.kt
@@ -0,0 +1,24 @@
+package de.platon42.intellij.plugins.m68k.documentation
+
+import com.intellij.lang.documentation.AbstractDocumentationProvider
+import com.intellij.lang.documentation.DocumentationMarkup
+import com.intellij.openapi.util.text.StringUtil
+import com.intellij.psi.PsiElement
+import de.platon42.intellij.plugins.m68k.psi.M68kAssignment
+import de.platon42.intellij.plugins.m68k.psi.M68kSymbolDefinition
+
+class M68kSymbolDefinitionDocumentationProvider : AbstractDocumentationProvider() {
+
+ override fun getQuickNavigateInfo(element: PsiElement, originalElement: PsiElement?): String? {
+ return generateDoc(element, originalElement)
+ }
+
+ override fun generateDoc(element: PsiElement, originalElement: PsiElement?): String? {
+ return if (element is M68kSymbolDefinition) {
+ // TODO find out how we can generate inner links for more symbol references inside the expression (DocumentationManagerUtil)
+ val value = (element.getParent() as M68kAssignment).expr.text
+ DocumentationMarkup.DEFINITION_START + StringUtil.escapeXmlEntities(element.name!!) + DocumentationMarkup.DEFINITION_END +
+ DocumentationMarkup.CONTENT_START + StringUtil.escapeXmlEntities(value) + DocumentationMarkup.CONTENT_END
+ } else null
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/inspections/M68kSyntaxInspection.kt b/src/main/java/de/platon42/intellij/plugins/m68k/inspections/M68kSyntaxInspection.kt
index 7e72f8b..f905445 100644
--- a/src/main/java/de/platon42/intellij/plugins/m68k/inspections/M68kSyntaxInspection.kt
+++ b/src/main/java/de/platon42/intellij/plugins/m68k/inspections/M68kSyntaxInspection.kt
@@ -5,7 +5,7 @@ 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.findMatchingInstructions
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
@@ -38,7 +38,7 @@ class M68kSyntaxInspection : AbstractBaseM68kLocalInspectionTool() {
override fun checkAsmInstruction(asmInstruction: M68kAsmInstruction, manager: InspectionManager, isOnTheFly: Boolean): Array? {
val asmOp = asmInstruction.asmOp
val mnemonicWithSize = asmOp.text
- val isaData = findMatchingInstruction(asmOp.mnemonic)
+ val isaData = findMatchingInstructions(asmOp.mnemonic)
if (isaData.isEmpty()) {
return arrayOf(
manager.createProblemDescriptor(
@@ -61,16 +61,19 @@ class M68kSyntaxInspection : AbstractBaseM68kLocalInspectionTool() {
)
)
}
- 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 amOp1 = asmInstruction.addressingModeList.getOrNull(0)
+ val amOp2 = asmInstruction.addressingModeList.getOrNull(1)
+ val op1 = getAddressModeForType(amOp1)
+ val op2 = getAddressModeForType(amOp2)
+ val specialReg1 = (amOp1 as? M68kSpecialRegisterDirectAddressingMode)?.specialRegister?.text
+ val specialReg2 = (amOp2 as? M68kSpecialRegisterDirectAddressingMode)?.specialRegister?.text
+ val specialReg = specialReg1 ?: specialReg2
val opSize = asmOp.opSize
- val matchingModeIsaData = findMatchingOpMode(isaData, op1, op2, opSize, specialReg1 ?: specialReg2)
+ val matchingModeIsaData = findMatchingOpMode(isaData, op1, op2, opSize, specialReg)
if (matchingModeIsaData.isEmpty()) {
- val matchingModeIsaDataIgnoringSize = findMatchingOpModeIgnoringSize(isaData, op1, op2, specialReg1 ?: specialReg2)
+ val matchingModeIsaDataIgnoringSize = findMatchingOpModeIgnoringSize(isaData, op1, op2, specialReg)
if (matchingModeIsaDataIgnoringSize.isEmpty()) {
- val matchingModeIsaDataSwapped = findMatchingOpModeIgnoringSize(isaData, op2, op1, specialReg1 ?: specialReg2)
+ val matchingModeIsaDataSwapped = findMatchingOpModeIgnoringSize(isaData, op2, op1, specialReg)
val supportedModesOp1 = isaData.flatMap { it.modes.flatMap { am -> am.op1 ?: emptySet() } }.toSet()
val supportedModesOp2 = isaData.flatMap { it.modes.flatMap { am -> am.op2 ?: emptySet() } }.toSet()
@@ -133,7 +136,7 @@ class M68kSyntaxInspection : AbstractBaseM68kLocalInspectionTool() {
}
- val supportedOpSizes = findSupportedOpSizes(matchingModeIsaDataIgnoringSize, op1, op2, specialReg1 ?: specialReg2)
+ val supportedOpSizes = findSupportedOpSizes(matchingModeIsaDataIgnoringSize, op1, op2, specialReg)
return arrayOf(
when (supportedOpSizes) {
OP_UNSIZED ->
diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/utils/M68kIsaUtil.kt b/src/main/java/de/platon42/intellij/plugins/m68k/utils/M68kIsaUtil.kt
new file mode 100644
index 0000000..dce2fc9
--- /dev/null
+++ b/src/main/java/de/platon42/intellij/plugins/m68k/utils/M68kIsaUtil.kt
@@ -0,0 +1,64 @@
+package de.platon42.intellij.plugins.m68k.utils
+
+import de.platon42.intellij.plugins.m68k.asm.*
+import de.platon42.intellij.plugins.m68k.psi.M68kAddressModeUtil
+import de.platon42.intellij.plugins.m68k.psi.M68kAsmInstruction
+import de.platon42.intellij.plugins.m68k.psi.M68kSpecialRegisterDirectAddressingMode
+
+object M68kIsaUtil {
+
+ // TODO if more than one result, do a check and consolidate
+ fun findExactIsaDataForInstruction(asmInstruction: M68kAsmInstruction): IsaData? =
+ findIsaDataForInstruction(asmInstruction).firstOrNull()?.first
+
+ // TODO if more than one result, do a check and consolidate
+ fun findExactIsaDataAndAllowedAdrModeForInstruction(asmInstruction: M68kAsmInstruction): Pair? {
+ val (isaData, adrModeList) = findIsaDataForInstruction(asmInstruction).firstOrNull() ?: return null
+ return isaData to adrModeList.first()
+ }
+
+ fun findIsaDataForInstruction(asmInstruction: M68kAsmInstruction): List>> {
+ val isaDataCandidates = M68kIsa.findMatchingInstructions(asmInstruction.asmOp.mnemonic)
+ if (isaDataCandidates.isEmpty()) return emptyList()
+
+ val amOp1 = asmInstruction.addressingModeList.getOrNull(0)
+ val amOp2 = asmInstruction.addressingModeList.getOrNull(1)
+ val op1 = M68kAddressModeUtil.getAddressModeForType(amOp1)
+ val op2 = M68kAddressModeUtil.getAddressModeForType(amOp2)
+ val specialReg1 = (amOp1 as? M68kSpecialRegisterDirectAddressingMode)?.specialRegister?.text
+ val specialReg2 = (amOp2 as? M68kSpecialRegisterDirectAddressingMode)?.specialRegister?.text
+ val specialReg = specialReg1 ?: specialReg2
+ val opSize = asmInstruction.asmOp.opSize
+ val matchedIsaData =
+ M68kIsa.findMatchingOpMode(isaDataCandidates, op1, op2, opSize, specialReg)
+ return matchedIsaData.map { it to M68kIsa.findMatchingAddressMode(it.modes, op1, op2, opSize, specialReg) }
+ }
+
+ fun findOpSizeDescription(opSize: Int): String {
+ return when (opSize) {
+ OP_UNSIZED -> ""
+ OP_SIZE_B -> ".b"
+ OP_SIZE_W -> ".w"
+ OP_SIZE_L -> ".l"
+ OP_SIZE_S -> ".s"
+ OP_SIZE_BWL -> ".b|.w|.l"
+ OP_SIZE_WL -> ".w|.l"
+ OP_SIZE_SBW -> ".s|.b|.w"
+ else -> "?"
+ }
+ }
+
+ fun findOpSizeDescriptions(opSize: Int): Set {
+ return when (opSize) {
+ OP_UNSIZED -> setOf("")
+ OP_SIZE_B -> setOf(".b")
+ OP_SIZE_W -> setOf(".w")
+ OP_SIZE_L -> setOf(".l")
+ OP_SIZE_S -> setOf(".s")
+ OP_SIZE_BWL -> setOf(".b", ".w", ".l")
+ OP_SIZE_WL -> setOf(".w", ".l")
+ OP_SIZE_SBW -> setOf(".s", ".b", ".w")
+ else -> setOf("?")
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index d743475..71c2579 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -10,7 +10,7 @@
Full documentation here...
]]>
-
+
com.intellij.modules.lang
@@ -33,6 +33,12 @@
implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kSymbolReferenceElementManipulator"/>
+
+
+
@@ -42,6 +48,7 @@
diff --git a/src/test/java/de/platon42/intellij/plugins/m68k/documentation/AbstractDocumentationProviderTest.kt b/src/test/java/de/platon42/intellij/plugins/m68k/documentation/AbstractDocumentationProviderTest.kt
new file mode 100644
index 0000000..ce402d8
--- /dev/null
+++ b/src/test/java/de/platon42/intellij/plugins/m68k/documentation/AbstractDocumentationProviderTest.kt
@@ -0,0 +1,20 @@
+package de.platon42.intellij.plugins.m68k.documentation
+
+import com.intellij.codeInsight.documentation.DocumentationManager
+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.junit.jupiter.api.extension.ExtendWith
+
+@ExtendWith(LightCodeInsightExtension::class)
+@TestDataPath("src/test/resources/documentation")
+abstract class AbstractDocumentationProviderTest : AbstractM68kTest() {
+
+ fun generateDocumentation(myFixture: CodeInsightTestFixture): String? {
+ val docElement = DocumentationManager.getInstance(myFixture.project).findTargetElement(myFixture.editor, myFixture.file)
+ val provider = DocumentationManager.getProviderFromElement(docElement)
+
+ return provider.generateDoc(docElement, myFixture.file.findElementAt(myFixture.caretOffset))
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/de/platon42/intellij/plugins/m68k/documentation/M68kInstructionDocumentationProviderTest.kt b/src/test/java/de/platon42/intellij/plugins/m68k/documentation/M68kInstructionDocumentationProviderTest.kt
new file mode 100644
index 0000000..23862f0
--- /dev/null
+++ b/src/test/java/de/platon42/intellij/plugins/m68k/documentation/M68kInstructionDocumentationProviderTest.kt
@@ -0,0 +1,59 @@
+package de.platon42.intellij.plugins.m68k.documentation
+
+import com.intellij.testFramework.fixtures.CodeInsightTestFixture
+import de.platon42.intellij.jupiter.LightCodeInsightExtension
+import de.platon42.intellij.jupiter.MyFixture
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+
+@ExtendWith(LightCodeInsightExtension::class)
+internal class M68kInstructionDocumentationProviderTest : AbstractDocumentationProviderTest() {
+
+ @Test
+ internal fun check_documentation_inside_mnemonic(@MyFixture myFixture: CodeInsightTestFixture) {
+ myFixture.configureByText(
+ "documentme.asm", """
+ moveq.l #40,d0
+ """
+ )
+ assertThat(generateDocumentation(myFixture)).isEqualTo(
+ """Mnemonic | Op1 | Op2 |
moveq.l | #<xxx> | Dn |
"""
+ )
+ }
+
+ @Test
+ internal fun check_documentation_inside_op_size(@MyFixture myFixture: CodeInsightTestFixture) {
+ myFixture.configureByText(
+ "documentme.asm", """
+ bra.s label
+ """
+ )
+ assertThat(generateDocumentation(myFixture)).isEqualTo(
+ """Mnemonic | Op1 | Op2 |
bra.s bra.b bra.w | (xxx).w|l | |
"""
+ )
+ }
+
+ @Test
+ internal fun check_documentation_for_privileged_instruction(@MyFixture myFixture: CodeInsightTestFixture) {
+ myFixture.configureByText(
+ "documentme.asm", """
+ reset
+ """
+ )
+ assertThat(generateDocumentation(myFixture)).isEqualTo(
+ """Reset External Devices
(privileged)
"""
+ )
+ }
+
+ @Test
+ internal fun check_documentation_if_there_is_no_concrete_match(@MyFixture myFixture: CodeInsightTestFixture) {
+ myFixture.configureByText(
+ "documentme.asm", """
+ cmp
+ """
+ )
+ assertThat(generateDocumentation(myFixture))
+ .contains("Compare", "Compare Address", "Compare Immediate", "Compare Memory to Memory")
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/de/platon42/intellij/plugins/m68k/documentation/M68kRegisterFlowDocumentationProviderTest.kt b/src/test/java/de/platon42/intellij/plugins/m68k/documentation/M68kRegisterFlowDocumentationProviderTest.kt
new file mode 100644
index 0000000..7350677
--- /dev/null
+++ b/src/test/java/de/platon42/intellij/plugins/m68k/documentation/M68kRegisterFlowDocumentationProviderTest.kt
@@ -0,0 +1,31 @@
+package de.platon42.intellij.plugins.m68k.documentation
+
+import com.intellij.testFramework.fixtures.CodeInsightTestFixture
+import de.platon42.intellij.jupiter.LightCodeInsightExtension
+import de.platon42.intellij.jupiter.MyFixture
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+
+@ExtendWith(LightCodeInsightExtension::class)
+internal class M68kRegisterFlowDocumentationProviderTest : AbstractDocumentationProviderTest() {
+
+ @Test
+ internal fun check_documentation_for_a_symbol_definition(@MyFixture myFixture: CodeInsightTestFixture) {
+ myFixture.configureByText(
+ "documentme.asm", """
+label
+ moveq.l #0,d0
+ add.w #1,d0
+ move.w d0,d1
+ move.b d2,d0
+ addq.b #1,d0
+ move.w d0,d1
+ move.l d0,d2
+ move.w d1,d2
+ rts
+ """
+ )
+ assertThat(generateDocumentation(myFixture)).isEqualTo("d0")
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/de/platon42/intellij/plugins/m68k/documentation/M68kSymbolDefinitionDocumentationProviderTest.kt b/src/test/java/de/platon42/intellij/plugins/m68k/documentation/M68kSymbolDefinitionDocumentationProviderTest.kt
new file mode 100644
index 0000000..2d4b3da
--- /dev/null
+++ b/src/test/java/de/platon42/intellij/plugins/m68k/documentation/M68kSymbolDefinitionDocumentationProviderTest.kt
@@ -0,0 +1,24 @@
+package de.platon42.intellij.plugins.m68k.documentation
+
+import com.intellij.testFramework.fixtures.CodeInsightTestFixture
+import de.platon42.intellij.jupiter.LightCodeInsightExtension
+import de.platon42.intellij.jupiter.MyFixture
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+
+@ExtendWith(LightCodeInsightExtension::class)
+internal class M68kSymbolDefinitionDocumentationProviderTest : AbstractDocumentationProviderTest() {
+
+ @Test
+ internal fun check_documentation_for_a_symbol_definition(@MyFixture myFixture: CodeInsightTestFixture) {
+ myFixture.configureByText(
+ "documentme.asm", """
+PIC_WIDTH = 320
+ move.w #PIC_WIDTH,d0
+ """
+ )
+ assertThat(generateDocumentation(myFixture))
+ .isEqualTo("320
")
+ }
+}
\ No newline at end of file