Added names for Adressing Modes.
Added Documentation Provider for symbol definitions (shows assigned declaration). Added Documentation Provider for mnemonics (simple version, generated out of ISA information). Work in progress for Register Flow Documentation Provider. New minimum required version of IDE is now 2020.3.
This commit is contained in:
parent
09a4e62c7c
commit
44a4721d77
@ -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)
|
||||
|
||||
|
@ -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("""
|
||||
<h4>V0.4 (unreleased)</h4>
|
||||
<ul>
|
||||
<li>Notice: Due to major new API use, this plugin no longer works on IDEs >=2019.3.1, but rather requires >=2020.3.
|
||||
<li>Enhancement: Added Structure View filters.
|
||||
<li>New: Added inspection to validate the correctness of a MC68000 instruction regarding operation size and address modes.
|
||||
<li>Bugfix: Added several missing assembler directives (opt, machine, etc.).
|
||||
<li>Bugfix: Uppercase hexadecimal literals were not parsed (JFlex bug?).
|
||||
<li>Bugfix: Interpretation of register lists was wrong in BNF.
|
||||
<li>New: Added Documentation Provider for symbol definitions (shows assigned declaration).
|
||||
<li>New: Added Documentation Provider for mnemonics (simple version, generated out of ISA information).
|
||||
</ul>
|
||||
<h4>V0.3 (28-Jul-21)</h4>
|
||||
<ul>
|
||||
|
@ -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", "#<xxx>"),
|
||||
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<IsaData> {
|
||||
fun findMatchingInstructions(mnemonic: String): List<IsaData> {
|
||||
val lowerMnemonic = mnemonic.lowercase()
|
||||
return isaData
|
||||
.filter {
|
||||
@ -755,6 +755,13 @@ object M68kIsa {
|
||||
}
|
||||
}
|
||||
|
||||
fun findMatchingAddressMode(modes: List<AllowedAdrMode>, op1: AddressMode?, op2: AddressMode?, opSize: Int?, specialReg: String?): List<AllowedAdrMode> {
|
||||
return modes.filter { 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) }
|
||||
|
@ -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<AddressMode>?): 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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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<ProblemDescriptor>? {
|
||||
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 ->
|
||||
|
@ -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<IsaData, AllowedAdrMode>? {
|
||||
val (isaData, adrModeList) = findIsaDataForInstruction(asmInstruction).firstOrNull() ?: return null
|
||||
return isaData to adrModeList.first()
|
||||
}
|
||||
|
||||
fun findIsaDataForInstruction(asmInstruction: M68kAsmInstruction): List<Pair<IsaData, List<AllowedAdrMode>>> {
|
||||
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<String> {
|
||||
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("?")
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@
|
||||
<p>
|
||||
<a href="https://github.com/chrisly42/mc68000-asm-plugin/blob/main/README.md">Full documentation here...</a>
|
||||
]]></description>
|
||||
<idea-version since-build="193.5233.103"/>
|
||||
<idea-version since-build="203.5981.166"/>
|
||||
|
||||
<depends>com.intellij.modules.lang</depends>
|
||||
|
||||
@ -33,6 +33,12 @@
|
||||
implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kSymbolReferenceElementManipulator"/>
|
||||
<lang.elementManipulator forClass="de.platon42.intellij.plugins.m68k.psi.M68kMacroCall"
|
||||
implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kMacroCallElementManipulator"/>
|
||||
<lang.documentationProvider language="MC68000"
|
||||
implementationClass="de.platon42.intellij.plugins.m68k.documentation.M68kSymbolDefinitionDocumentationProvider"/>
|
||||
<lang.documentationProvider language="MC68000"
|
||||
implementationClass="de.platon42.intellij.plugins.m68k.documentation.M68kRegisterFlowDocumentationProvider"/>
|
||||
<lang.documentationProvider language="MC68000"
|
||||
implementationClass="de.platon42.intellij.plugins.m68k.documentation.M68kInstructionDocumentationProvider"/>
|
||||
<stubElementTypeHolder class="de.platon42.intellij.plugins.m68k.stubs.M68kElementTypes"/>
|
||||
<stubIndex implementation="de.platon42.intellij.plugins.m68k.stubs.M68kGlobalLabelStubIndex"/>
|
||||
<stubIndex implementation="de.platon42.intellij.plugins.m68k.stubs.M68kSymbolDefinitionStubIndex"/>
|
||||
@ -42,6 +48,7 @@
|
||||
<renameInputValidator implementation="de.platon42.intellij.plugins.m68k.psi.M68kRenameInputValidator"/>
|
||||
|
||||
<localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kSyntaxInspection"
|
||||
displayName="Assembly instruction validity" groupName="M68k"
|
||||
enabledByDefault="true" level="ERROR"/>
|
||||
</extensions>
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
@ -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", """
|
||||
mo<caret>veq.l #40,d0
|
||||
"""
|
||||
)
|
||||
assertThat(generateDocumentation(myFixture)).isEqualTo(
|
||||
"""<div class="definition"><pre><b>Move Quick</b></pre></div><div class="content"><table class="sections"><tr><td class="section" valign="top">Mnemonic</td><td class="section" valign="top">Op1</td><td class="section" valign="top">Op2</td></tr><tr><td valign="top"><p>moveq.l</p></td><td valign="top"><p>#<xxx></p></td><td valign="top"><p>Dn</p></td></tr><br/></table></div>"""
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun check_documentation_inside_op_size(@MyFixture myFixture: CodeInsightTestFixture) {
|
||||
myFixture.configureByText(
|
||||
"documentme.asm", """
|
||||
bra.<caret>s label
|
||||
"""
|
||||
)
|
||||
assertThat(generateDocumentation(myFixture)).isEqualTo(
|
||||
"""<div class="definition"><pre><b>Branch</b></pre></div><div class="content"><table class="sections"><tr><td class="section" valign="top">Mnemonic</td><td class="section" valign="top">Op1</td><td class="section" valign="top">Op2</td></tr><tr><td valign="top"><p>bra.s</p><p>bra.b</p><p>bra.w</p></td><td valign="top"><p>(xxx).w|l</p></td><td valign="top"></td></tr><br/></table></div>"""
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun check_documentation_for_privileged_instruction(@MyFixture myFixture: CodeInsightTestFixture) {
|
||||
myFixture.configureByText(
|
||||
"documentme.asm", """
|
||||
<caret>reset
|
||||
"""
|
||||
)
|
||||
assertThat(generateDocumentation(myFixture)).isEqualTo(
|
||||
"""<div class="definition"><pre><b>Reset External Devices</b></pre><p><font color="red">(privileged)</font></p></div><div class="content"><table class="sections"><tr><td class="section" valign="top">Mnemonic</td><td class="section" valign="top">Op1</td><td class="section" valign="top">Op2</td></tr><tr><td valign="top"><p>reset</p></td><td valign="top"></td><td valign="top"></td></tr><br/></table></div>"""
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun check_documentation_if_there_is_no_concrete_match(@MyFixture myFixture: CodeInsightTestFixture) {
|
||||
myFixture.configureByText(
|
||||
"documentme.asm", """
|
||||
<caret>cmp
|
||||
"""
|
||||
)
|
||||
assertThat(generateDocumentation(myFixture))
|
||||
.contains("<b>Compare</b>", "<b>Compare Address</b>", "<b>Compare Immediate</b>", "<b>Compare Memory to Memory</b>")
|
||||
}
|
||||
}
|
@ -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 d<caret>0,d1
|
||||
move.l d0,d2
|
||||
move.w d1,d2
|
||||
rts
|
||||
"""
|
||||
)
|
||||
assertThat(generateDocumentation(myFixture)).isEqualTo("d0")
|
||||
}
|
||||
}
|
@ -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_WIDT<caret>H,d0
|
||||
"""
|
||||
)
|
||||
assertThat(generateDocumentation(myFixture))
|
||||
.isEqualTo("<div class='definition'><pre>PIC_WIDTH</pre></div><div class='content'>320</div>")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user