9 Commits
Author SHA1 Message Date
chrisly42 2c3daf28f7 TODO list. Prepared next release (for tomorrow). 2021-08-05 19:02:55 +02:00
chrisly42 1dcf288d27 Added inspection to warn about unexpected condition code unaffecting instructions before conditional instructions.
Extended documentation.
Bugfix in M68kDeadWriteInspection.
2021-08-05 18:42:32 +02:00
chrisly42 2abb5af8b0 Macros with register name now abort Dead Write analysis. Fix for modifying statements extending the with of the written data. 2021-08-05 16:21:32 +02:00
chrisly42 6c9a726b86 Preprocessor statements now abort Dead Write analysis. 2021-08-05 15:46:16 +02:00
chrisly42 921449cbb8 Refactored stuff.
Added control flow information to ISA.
In ISA exg is no longer treated as setting a definitive value.
Added inspection find dead writes to registers.
2021-08-04 17:39:54 +02:00
chrisly42 6f99c2ffcc Moved Condition-Code stuff to separate file, added reverse mapping function.
Reworked Instruction Documentation provider, now shows condition codes.
Cosmetics: Removed MutableList information where unnecessary
2021-08-04 12:24:16 +02:00
chrisly42 5881dcdaf8 Optimized mnemonic lookup. 2021-08-03 18:28:53 +02:00
chrisly42 71398f51d2 Reworked and extended ISA to hold condition code testing and affecting information (not used yet).
Minor fix for `andi/eori/ori to ccr` which were not byte sized in ISA.
Added alternate condition code tests HS (=CC) and LO (=CS).
2021-08-03 13:45:44 +02:00
chrisly42 eb26793a20 movem ISA was wrong regarding movem.w <ea>,<registerlist> (sign extends registers).
Changed Register Flow Documentation wording from 'reads' to 'uses' and from 'modifies' to 'changes'.
Updated screenshot. Added another test case for M68kRegisterFlowDocumentationProviderTest.
2021-08-03 10:57:53 +02:00
27 changed files with 1332 additions and 252 deletions
+92 -7
View File
@@ -2,7 +2,7 @@
_MC68000 Assembly Language Plugin_ is plugin for Jetbrains IDEs (CLion, IntelliJ, etc.). _MC68000 Assembly Language Plugin_ is plugin for Jetbrains IDEs (CLion, IntelliJ, etc.).
![Example Syntax Highlighting](docs/syntaxhighlighting.png "Example Syntax Highlighting") ![Example IDE Screenshot](docs/example.png "Example IDE Screenshot")
## Purpose ## Purpose
@@ -19,13 +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. 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 two weeks of work. 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 a few weeks of work. I released a really early first version it because I think it's "
it's "good enough" to get started, and I can return to demo coding with its current state. good enough" to get started, and I can return to demo coding with its current state.
## Features ## Features
- Parser / Lexer for MC68000 (yes, only 68000 right now!) assembly language files in VAsm / DevPac style - Parser / Lexer for MC68000 (yes, only 68000 right now!) assembly language files in VAsm / DevPac style
- Inspection for validating the syntax the 68000 ISA. - Inspection for validating the syntax of the 68000 ISA.
- Syntax highlighting and Color Settings Page (you should really modify the color settings to your likings!) - Syntax highlighting and Color Settings Page (you should really modify the color settings to your likings!)
- Mnemonics code completion - Mnemonics code completion
- Symbols / Labels / Macros code completion - Symbols / Labels / Macros code completion
@@ -37,6 +37,70 @@ it's "good enough" to get started, and I can return to demo coding with its curr
- Structure view - Structure view
- Documentation provider for symbol definitions and mnemonics (listing available addressing modes etc.). - Documentation provider for symbol definitions and mnemonics (listing available addressing modes etc.).
### Inspections
#### M68kSyntaxInspection - Assembly instruction validity
Checks the validity of the current instruction. If an instruction is not recognized, you may get one of the following errors:
- Instruction _mnemonic_ not supported on selected cpu (you won't get this currently as only MC68000 is supported)
- No operands expected for _mnemonic_
- Second operand _op_ unexpected for _mnemonic_
- Unsupported addressing mode for _mnemonic_
- Unsupported addressing mode _op_ for first operand of _mnemonic_
- Unsupported addressing mode _op_ for second operand of _mnemonic_
- Unsupported addressing modes for operands in this order for _mnemonic_ (try swapping)
- Instruction _mnemonic_ is unsized (you tried to specify `.b`, `.w` or `.l`)
- Operation size _(.b,.w,.l)_ unsupported for _mnemonic_
- Operation size _(.b,.w,.l)_ unsupported (should be _(.b,.w,.l)_)
#### M68kDeadWriteInspection - Dead writes to registers
This inspection looks at register writes and tries to find instructions that renders a write moot because it was overwritten by another instruction before
anything useful was done with it.
Analysis is aborted at global labels, flow control instructions, directives
(e.g. conditional assembly) and macros with the register names as parameter.
The inspection tries to take condition code changing into account and puts out a weak warning if the statement merely changes condition codes before the
contents of the register are overwritten. In this case, it is sometimes better to replace `move` by `tst`.
#### M68kUnexpectedConditionalInstructionInspection - Unaffected condition codes before conditional instruction
Especially for novice coders, it is not clear that some instructions do not affect the condition codes for a subsequent condition branch or `scc` instruction.
`movea`, `adda` and `suba` come to my mind.
The inspection will report such suspicious instruction sequences.
However, this does not need to be a programming error. Advanced coders sometimes make use of the fact that instructions do not change condition codes and thus
optimize the order of execution.
### Documentation provider
#### M68kSymbolDefinitionDocumentationProvider
Provides the assigned value of a `=`, `set` or `equ` symbol definition when hovering over a symbol.
#### M68kRegisterFlowDocumentationProvider
When hovering over or placing the cursor at a data or address register, the documentation will scan through the instructions backwards and forwards and will
show all read, changes of the register contents. It does this until an instruction is found that defines (sets) the contents of the register
(according to the size of the instruction where the cursor was placed).
The analysis ignores all code flow instructions and might be inaccurate for branches, macro use, etc. It also stops at global labels.
The documentation will search up to 100 instructions in each direction, but only four when hovering over the register
(so if you need the whole analysis, use the documentation window).
#### M68kInstructionDocumentationProvider
When hovering over a mnemonic, it will show a short description of the assembly instruction.
For the documentation window, affected condition codes, allowed operation sizes and addressing modes are listed for the selected instruction
(so only stuff from `cmpa` is listed when you're looking at a `cmp.w a0,a1` instruction).
If the current statement has no valid syntax, the instruction details of all matching mnemonics will be shown instead.
## Known issues ## Known issues
- `Find Usages` always shows _"Unclassified"_ though it shouldn't (?) - `Find Usages` always shows _"Unclassified"_ though it shouldn't (?)
@@ -46,13 +110,13 @@ it's "good enough" to get started, and I can return to demo coding with its curr
- While the Lexer supports the -spaces option (where a space introduces a comment), this cannot be configured yet (default is off). - 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. - No support for other processor instructions, FPU or 68020+ address modes.
- Unit Test coverage is not as good as it could be (ahem). - Unit Test coverage is not as good as it could be (ahem).
- `opt` keyword needs special treatment and will currently show a parsing error - `opt` keyword needs special treatment and will currently show a parsing error.
- Missing but planned features: - Missing but planned features:
- Macro evaluation on invocation - Macro evaluation on invocation
- Folding - Folding
- Semantic inspections - More semantic inspections
- Quick fixes - Quick fixes
- Formatter + Code Style Settings - Maybe formatter + Code Style Settings
- Register use analysis (but this only makes sense after macro evaluation) - Register use analysis (but this only makes sense after macro evaluation)
- Cycle counting - Cycle counting
@@ -69,8 +133,29 @@ It is probably the only plugin (besides [Cajon](https://github.com/chrisly42/caj
far (or at least the only one I'm aware of ;) ). The IntelliJ framework actually uses the JUnit 3 TestCase for plugin testing, and it took me quite a while to far (or at least the only one I'm aware of ;) ). The IntelliJ framework actually uses the JUnit 3 TestCase for plugin testing, and it took me quite a while to
make it work with JUnit 5. Feel free to use the code (in package ```de.platon42.intellij.jupiter```) for your projects (with attribution). make it work with JUnit 5. Feel free to use the code (in package ```de.platon42.intellij.jupiter```) for your projects (with attribution).
## Private TODO list
- code completion suggestion for unresolved local labels, global labels and symbols
- support `include` directive
- support `opt` directive
- suppression support via comments
- inspection: Unresolved local label
- inspection: Unresolved macro
## Changelog ## Changelog
### V0.5 (06-Aug-21)
- Bugfix: `movem` ISA was wrong regarding the `movem.w <ea>,<registerlist>` (sign extends registers).
- Cosmetics: Changed Register Flow Documentation wording from _reads_ to _uses_ and from _modifies_ to _changes_.
- Bugfix: Minor fix for `andi/eori/ori to ccr` which were not byte sized in ISA.
- Bugfix: Added alternate condition code tests `HS (=CC)` and `LO (=CS)`.
- Performance: Optimized mnemonic lookup.
- Enhancement: Reworked Instruction Documentation provider, now shows condition codes.
- Bugfix: In ISA `exg` is no longer treated as setting a definitive value.
- New: Added inspection to find dead writes to registers!
- New: Added inspection to warn about unexpected condition code unaffecting instructions before conditional instructions.
### V0.4 (03-Aug-21) ### V0.4 (03-Aug-21)
- Notice: Due to major new API use, this plugin no longer works on IDEs >=2019.3.1, but rather requires >=2020.3. - Notice: Due to major new API use, this plugin no longer works on IDEs >=2019.3.1, but rather requires >=2020.3.
+13 -21
View File
@@ -7,7 +7,7 @@ plugins {
} }
group = 'de.platon42' group = 'de.platon42'
version = '0.4' version = '0.5'
sourceCompatibility = "1.8" sourceCompatibility = "1.8"
targetCompatibility = "1.8" targetCompatibility = "1.8"
@@ -57,6 +57,18 @@ runPluginVerifier {
patchPluginXml { patchPluginXml {
setChangeNotes(""" setChangeNotes("""
<h4>V0.5 (06-Aug-21)</h4>
<ul>
<li>Bugfix: movem ISA was wrong regarding movem.w <ea>,<registerlist> (sign extends registers).
<li>Cosmetics: Changed Register Flow Documentation wording from 'reads' to 'uses' and from 'modifies' to 'changes'.
<li>Bugfix: Minor fix for `andi/eori/ori to ccr` which were not byte sized in ISA.
<li>Bugfix: Added alternate condition code tests HS (=CC) and LO (=CS).
<li>Performance: Optimized mnemonic lookup.
<li>Enhancement: Reworked Instruction Documentation provider, now shows condition codes.
<li>Bugfix: In ISA exg is no longer treated as setting a definitive value.
<li>New: Added inspection to find dead writes to registers!
<li>New: Added inspection to warn about unexpected condition code unaffecting instructions before conditional instructions.
</ul>
<h4>V0.4 (03-Aug-21)</h4> <h4>V0.4 (03-Aug-21)</h4>
<ul> <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>Notice: Due to major new API use, this plugin no longer works on IDEs >=2019.3.1, but rather requires >=2020.3.
@@ -70,26 +82,6 @@ patchPluginXml {
<li>Bugfix: Macro definitions with colons and without space supported (as found in P61a source). <li>Bugfix: Macro definitions with colons and without space supported (as found in P61a source).
<li>New: When asking for documentation on registers, a code flow analysis is done. Cool stuff! <li>New: When asking for documentation on registers, a code flow analysis is done. Cool stuff!
</ul> </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>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.
<li>Performance: Reference search for global labels and symbols now uses stub index.
<li>Compatibility: Restored compatibility with IDE versions < 2021.1.
<li>Performance: Optimized lexer.
</ul>
<h4>V0.2 (27-Jul-21)</h4>
<ul>
<li>Cosmetics: Added (same) icon for plugin as for file type.
<li>Performance: Use Word-Index for global labels and symbols instead of iterating over the file.
<li>Performance: Use Stub-Index for global labels and symbols.
<li>Bugfix: No longer reports a syntax error when file lacks terminating End-Of-Line.
<li>Enhancement: Registers are now offered for code completion, making editing less annoying.
</ul>
<p>Full changelog available at <a href="https://github.com/chrisly42/mc68000-asm-plugin#changelog">Github project site</a>.</p> <p>Full changelog available at <a href="https://github.com/chrisly42/mc68000-asm-plugin#changelog">Github project site</a>.</p>
""") """)
} }
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

@@ -0,0 +1,198 @@
package de.platon42.intellij.plugins.m68k.asm
const val CC_X_CLEAR = 0x10000
const val CC_X_SET = 0x20000
const val CC_X_UNDEF = 0x30000
const val CC_X_RES = 0x40000
const val CC_X_AND = 0x50000
const val CC_X_OR = 0x60000
const val CC_X_CARRY = 0x70000
const val CC_X_TST = 0xf0000
const val CC_N_CLEAR = 0x01000
const val CC_N_SET = 0x02000
const val CC_N_UNDEF = 0x03000
const val CC_N_RES = 0x04000
const val CC_N_AND = 0x05000
const val CC_N_OR = 0x06000
const val CC_N_TST = 0x0f000
const val CC_Z_CLEAR = 0x00100
const val CC_Z_SET = 0x00200
const val CC_Z_UNDEF = 0x00300
const val CC_Z_RES = 0x00400
const val CC_Z_AND = 0x00500
const val CC_Z_OR = 0x00600
const val CC_Z_TST = 0x00f00
const val CC_V_CLEAR = 0x00010
const val CC_V_SET = 0x00020
const val CC_V_UNDEF = 0x00030
const val CC_V_RES = 0x00040
const val CC_V_AND = 0x00050
const val CC_V_OR = 0x00060
const val CC_V_TST = 0x000f0
const val CC_C_CLEAR = 0x00001
const val CC_C_SET = 0x00002
const val CC_C_UNDEF = 0x00003
const val CC_C_RES = 0x00004
const val CC_C_AND = 0x00005
const val CC_C_OR = 0x00006
const val CC_C_TST = 0x0000f
private const val CC_NOT_AFFECTED_STR = "Not affected"
private const val CC_ALWAYS_CLEAR_STR = "Always cleared"
private const val CC_ALWAYS_SET_STR = "Always set"
private const val CC_UNDEFINED_STR = "Undefined"
private const val CC_RES_STR = "From result"
private const val CC_AND_STR = "And'ed: Only cleared for zero bit"
private const val CC_OR_STR = "Or'ed: Only set for one bit"
fun getCcInfo(cc: Int): Map<String, Pair<String, String>> {
val xnzvcMap = LinkedHashMap<String, Pair<String, String>>(5)
xnzvcMap["X"] = when (cc and CC_X_TST) {
0 -> "-" to CC_NOT_AFFECTED_STR
CC_X_SET -> "1" to CC_ALWAYS_SET_STR
CC_X_CLEAR -> "0" to CC_ALWAYS_CLEAR_STR
CC_X_UNDEF -> "U" to CC_UNDEFINED_STR
CC_X_AND -> "*" to CC_AND_STR
CC_X_OR -> "*" to CC_OR_STR
CC_X_CARRY -> "*" to "Set the same as the carry bit"
else -> "*" to "$CC_RES_STR (usually the bit shifted out)"
}
xnzvcMap["N"] = when (cc and CC_N_TST) {
0 -> "-" to CC_NOT_AFFECTED_STR
CC_N_SET -> "1" to CC_ALWAYS_SET_STR
CC_N_CLEAR -> "0" to CC_ALWAYS_CLEAR_STR
CC_N_UNDEF -> "U" to CC_UNDEFINED_STR
CC_N_AND -> "*" to CC_AND_STR
CC_N_OR -> "*" to CC_OR_STR
else -> "*" to "$CC_RES_STR (usually if negative)"
}
xnzvcMap["Z"] = when (cc and CC_Z_TST) {
0 -> "-" to CC_NOT_AFFECTED_STR
CC_Z_SET -> "1" to CC_ALWAYS_SET_STR
CC_Z_CLEAR -> "0" to CC_ALWAYS_CLEAR_STR
CC_Z_UNDEF -> "U" to CC_UNDEFINED_STR
CC_Z_AND -> "*" to CC_AND_STR
CC_Z_OR -> "*" to CC_OR_STR
else -> "*" to "$CC_RES_STR (usually if zero)"
}
xnzvcMap["V"] = when (cc and CC_V_TST) {
0 -> "-" to CC_NOT_AFFECTED_STR
CC_V_SET -> "1" to CC_ALWAYS_SET_STR
CC_V_CLEAR -> "0" to CC_ALWAYS_CLEAR_STR
CC_V_UNDEF -> "U" to CC_UNDEFINED_STR
CC_V_AND -> "*" to CC_AND_STR
CC_V_OR -> "*" to CC_OR_STR
else -> "*" to "$CC_RES_STR (usually for overflows)"
}
xnzvcMap["C"] = when (cc and CC_V_TST) {
0 -> "-" to CC_NOT_AFFECTED_STR
CC_V_SET -> "1" to CC_ALWAYS_SET_STR
CC_V_CLEAR -> "0" to CC_ALWAYS_CLEAR_STR
CC_V_UNDEF -> "U" to CC_UNDEFINED_STR
CC_V_AND -> "*" to CC_AND_STR
CC_V_OR -> "*" to CC_OR_STR
else -> "*" to "$CC_RES_STR (usually carry/borrow)"
}
return xnzvcMap
}
fun cc(xnzvc: String): Int {
var result = 0
result += when (xnzvc[0]) {
'-' -> 0
'0' -> CC_X_CLEAR
'1' -> CC_X_SET
'U' -> CC_X_UNDEF
'*' -> CC_X_RES
'A' -> CC_X_AND
'O' -> CC_X_OR
'C' -> CC_X_CARRY
'?' -> CC_X_TST
else -> throw IllegalArgumentException("Syntax Error")
}
result += when (xnzvc[1]) {
'-' -> 0
'0' -> CC_N_CLEAR
'1' -> CC_N_SET
'U' -> CC_N_UNDEF
'*' -> CC_N_RES
'A' -> CC_N_AND
'O' -> CC_N_OR
'?' -> CC_N_TST
else -> throw IllegalArgumentException("Syntax Error")
}
result += when (xnzvc[2]) {
'-' -> 0
'0' -> CC_Z_CLEAR
'1' -> CC_Z_SET
'U' -> CC_Z_UNDEF
'*' -> CC_Z_RES
'A' -> CC_Z_AND
'O' -> CC_Z_OR
'?' -> CC_Z_TST
else -> throw IllegalArgumentException("Syntax Error")
}
result += when (xnzvc[3]) {
'-' -> 0
'0' -> CC_V_CLEAR
'1' -> CC_V_SET
'U' -> CC_V_UNDEF
'*' -> CC_V_RES
'A' -> CC_V_AND
'O' -> CC_V_OR
'?' -> CC_V_TST
else -> throw IllegalArgumentException("Syntax Error")
}
result += when (xnzvc[4]) {
'-' -> 0
'0' -> CC_C_CLEAR
'1' -> CC_C_SET
'U' -> CC_C_UNDEF
'*' -> CC_C_RES
'A' -> CC_C_AND
'O' -> CC_C_OR
'?' -> CC_C_TST
else -> throw IllegalArgumentException("Syntax Error")
}
return result
}
enum class ConditionCode(val cc: String, val testedCc: Int) {
TRUE("t", cc("-----")),
FALSE("f", cc("-----")),
HI("hi", cc("--?-?")),
LS("ls", cc("--?-?")),
CC("cc", cc("----?")),
HS("hs", cc("----?")), // same as CC
CS("cs", cc("----?")),
LO("lo", cc("----?")), // same as CS
NE("ne", cc("--?--")),
EQ("eq", cc("--?--")),
VC("vc", cc("---?-")),
VS("vs", cc("---?-")),
PL("pl", cc("-?---")),
MI("mi", cc("-?---")),
GE("ge", cc("-?-?-")),
LT("lt", cc("-?-?-")),
GT("gt", cc("-???-")),
LE("le", cc("-???-"));
companion object {
private val NAME_TO_CC_MAP = values().associateBy { it.cc }
fun getCcFromName(cc: String) = NAME_TO_CC_MAP[cc.lowercase()]!!
fun getCcFromMnemonic(originalMnemonic: String, mnemonic: String) =
// handle special case for dbra
if (mnemonic.equals("dbra", ignoreCase = true)) {
FALSE
} else {
NAME_TO_CC_MAP[mnemonic.removePrefix(originalMnemonic.removeSuffix("CC")).lowercase()]!!
}
}
}
@@ -72,11 +72,13 @@ const val RWM_READ_OPSIZE = 0x800
const val RWM_READ_B = 0x900 const val RWM_READ_B = 0x900
const val RWM_READ_W = 0xb00 const val RWM_READ_W = 0xb00
const val RWM_READ_L = 0xf00 const val RWM_READ_L = 0xf00
const val RWM_READ_SHIFT = 8
const val RWM_MODIFY_OPSIZE = 0x880 const val RWM_MODIFY_OPSIZE = 0x080
const val RWM_MODIFY_B = 0x990 const val RWM_MODIFY_B = 0x090
const val RWM_MODIFY_W = 0xbb0 const val RWM_MODIFY_W = 0x0b0
const val RWM_MODIFY_L = 0xff0 const val RWM_MODIFY_L = 0x0f0
const val RWM_MODIFY_SHIFT = 4
const val RWM_OP1_SHIFT = 0 const val RWM_OP1_SHIFT = 0
const val RWM_OP2_SHIFT = 12 const val RWM_OP2_SHIFT = 12
@@ -118,7 +120,9 @@ data class AllowedAdrMode(
val op2: Set<AddressMode>? = null, val op2: Set<AddressMode>? = null,
val size: Int = OP_SIZE_BWL, val size: Int = OP_SIZE_BWL,
val specialReg: String? = null, val specialReg: String? = null,
val modInfo: Int = 0 val modInfo: Int = 0,
val affectedCc: Int = 0,
val testedCc: Int = 0
) )
data class IsaData( data class IsaData(
@@ -131,6 +135,7 @@ data class IsaData(
val isPrivileged: Boolean = false, val isPrivileged: Boolean = false,
val hasOps: Boolean = true, val hasOps: Boolean = true,
val modes: List<AllowedAdrMode> = listOf(AllowedAdrMode()), val modes: List<AllowedAdrMode> = listOf(AllowedAdrMode()),
val changesControlFlow: Boolean = false
) )
object M68kIsa { object M68kIsa {
@@ -210,38 +215,78 @@ object M68kIsa {
private val DREG_ONLY = setOf(AddressMode.DATA_REGISTER_DIRECT) private val DREG_ONLY = setOf(AddressMode.DATA_REGISTER_DIRECT)
private val ADD_SUB_MODES = listOf( private val ADD_SUB_MODES = listOf(
AllowedAdrMode(ALL_EXCEPT_AREG, setOf(AddressMode.DATA_REGISTER_DIRECT), modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE), AllowedAdrMode(
ALL_EXCEPT_AREG,
setOf(AddressMode.DATA_REGISTER_DIRECT),
modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE,
affectedCc = cc("C****")
),
AllowedAdrMode( AllowedAdrMode(
setOf(AddressMode.ADDRESS_REGISTER_DIRECT), setOf(AddressMode.ADDRESS_REGISTER_DIRECT),
setOf(AddressMode.DATA_REGISTER_DIRECT), setOf(AddressMode.DATA_REGISTER_DIRECT),
OP_SIZE_WL, OP_SIZE_WL,
modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE,
affectedCc = cc("C****")
),
AllowedAdrMode(
setOf(AddressMode.DATA_REGISTER_DIRECT),
INDIRECT_MODES,
modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE,
affectedCc = cc("C****")
), ),
AllowedAdrMode(setOf(AddressMode.DATA_REGISTER_DIRECT), INDIRECT_MODES, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE),
) )
private val ASD_LSD_ROD_ROXD_MODES = listOf( private val ADDQ_SUBQ_MODES = listOf(
AllowedAdrMode(DREG_ONLY, DREG_ONLY, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE), AllowedAdrMode(
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), DREG_ONLY, modInfo = RWM_MODIFY_OP2_OPSIZE), setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, modInfo = RWM_MODIFY_OP2_OPSIZE, affectedCc = cc("C****")
AllowedAdrMode(INDIRECT_MODES, null, modInfo = RWM_MODIFY_OP1_OPSIZE), ),
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), AREG_ONLY, size = OP_SIZE_WL, modInfo = RWM_MODIFY_OP2_L, affectedCc = cc("C****"))
)
private val ADDX_SUBX_MODES = listOf(
AllowedAdrMode(DREG_ONLY, DREG_ONLY, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE, affectedCc = cc("C*A**"), testedCc = cc("?-?--")),
AllowedAdrMode(
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC),
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC),
modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE,
affectedCc = cc("C*A**"), testedCc = cc("?-?--")
)
)
private val ASD_LSD_MODES = listOf(
AllowedAdrMode(DREG_ONLY, DREG_ONLY, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE, affectedCc = cc("*****")),
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), DREG_ONLY, modInfo = RWM_MODIFY_OP2_OPSIZE, affectedCc = cc("*****")),
AllowedAdrMode(INDIRECT_MODES, null, modInfo = RWM_MODIFY_OP1_OPSIZE, affectedCc = cc("*****")),
)
private val ROD_MODES = listOf(
AllowedAdrMode(DREG_ONLY, DREG_ONLY, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE, affectedCc = cc("-**0*")),
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), DREG_ONLY, modInfo = RWM_MODIFY_OP2_OPSIZE, affectedCc = cc("-**0*")),
AllowedAdrMode(INDIRECT_MODES, null, modInfo = RWM_MODIFY_OP1_OPSIZE, affectedCc = cc("-**0*")),
)
private val ROXD_MODES = listOf(
AllowedAdrMode(DREG_ONLY, DREG_ONLY, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE, affectedCc = cc("***0*"), testedCc = cc("?----")),
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), DREG_ONLY, modInfo = RWM_MODIFY_OP2_OPSIZE, affectedCc = cc("***0*"), testedCc = cc("?----")),
AllowedAdrMode(INDIRECT_MODES, null, modInfo = RWM_MODIFY_OP1_OPSIZE, affectedCc = cc("***0*"), testedCc = cc("?----")),
) )
private val BCHG_BCLR_BSET_MODES = listOf( private val BCHG_BCLR_BSET_MODES = listOf(
AllowedAdrMode(DREG_ONLY, DREG_ONLY, OP_SIZE_L, modInfo = RWM_READ_OP1_B or RWM_MODIFY_OP2_L), AllowedAdrMode(DREG_ONLY, DREG_ONLY, OP_SIZE_L, modInfo = RWM_READ_OP1_B or RWM_MODIFY_OP2_L, affectedCc = cc("--*--")),
AllowedAdrMode(DREG_ONLY, INDIRECT_MODES, OP_SIZE_B, modInfo = RWM_READ_OP1_B or RWM_MODIFY_OP2_B), AllowedAdrMode(DREG_ONLY, INDIRECT_MODES, OP_SIZE_B, modInfo = RWM_READ_OP1_B or RWM_MODIFY_OP2_B, affectedCc = cc("--*--")),
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), DREG_ONLY, OP_SIZE_L, modInfo = RWM_MODIFY_OP2_L), AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), DREG_ONLY, OP_SIZE_L, modInfo = RWM_MODIFY_OP2_L, affectedCc = cc("--*--")),
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), INDIRECT_MODES, OP_SIZE_B, modInfo = RWM_MODIFY_OP2_B), AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), INDIRECT_MODES, OP_SIZE_B, modInfo = RWM_MODIFY_OP2_B, affectedCc = cc("--*--")),
) )
private val BTST_MODES = listOf( private val BTST_MODES = listOf(
AllowedAdrMode(DREG_ONLY, DREG_ONLY, OP_SIZE_L, modInfo = RWM_READ_OP1_B or RWM_READ_OP2_L), AllowedAdrMode(DREG_ONLY, DREG_ONLY, OP_SIZE_L, modInfo = RWM_READ_OP1_B or RWM_READ_OP2_L, affectedCc = cc("--*--")),
AllowedAdrMode(DREG_ONLY, INDIRECT_MODES, OP_SIZE_B, modInfo = RWM_READ_OP1_B or RWM_READ_OP2_B), AllowedAdrMode(DREG_ONLY, INDIRECT_MODES, OP_SIZE_B, modInfo = RWM_READ_OP1_B or RWM_READ_OP2_B, affectedCc = cc("--*--")),
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), DREG_ONLY, OP_SIZE_L, modInfo = RWM_READ_OP2_L), AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), DREG_ONLY, OP_SIZE_L, modInfo = RWM_READ_OP2_L, affectedCc = cc("--*--")),
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), INDIRECT_MODES, OP_SIZE_B, modInfo = RWM_READ_OP2_B), AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), INDIRECT_MODES, OP_SIZE_B, modInfo = RWM_READ_OP2_B, affectedCc = cc("--*--")),
) )
private val conditionCodes = private val conditionCodes =
listOf("cc", "ls", "cs", "lt", "eq", "mi", "f", "ne", "ge", "pl", "gt", "t", "hi", "vc", "le", "vs") listOf("cc", "hs", "ls", "cs", "lo", "lt", "eq", "mi", "f", "ne", "ge", "pl", "gt", "t", "hi", "vc", "le", "vs")
private val conditionCodesBcc = conditionCodes.filterNot { it == "f" || it == "t" } private val conditionCodesBcc = conditionCodes.filterNot { it == "f" || it == "t" }
@@ -250,7 +295,12 @@ object M68kIsa {
// Data Movement Instructions // Data Movement Instructions
IsaData( IsaData(
"move", "Move", "move", "Move",
modes = listOf(AllowedAdrMode(ALL_68000_MODES, ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, modInfo = RWM_READ_OP1_OPSIZE or RWM_SET_OP2_OPSIZE)) modes = listOf(
AllowedAdrMode(
ALL_68000_MODES, ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, modInfo = RWM_READ_OP1_OPSIZE or RWM_SET_OP2_OPSIZE,
affectedCc = cc("-**00")
)
)
), ),
IsaData( IsaData(
"movea", "Move Address", altMnemonics = listOf("move"), "movea", "Move Address", altMnemonics = listOf("move"),
@@ -283,7 +333,7 @@ object M68kIsa {
), ),
setOf(AddressMode.REGISTER_LIST, AddressMode.ADDRESS_REGISTER_DIRECT, AddressMode.DATA_REGISTER_DIRECT), setOf(AddressMode.REGISTER_LIST, AddressMode.ADDRESS_REGISTER_DIRECT, AddressMode.DATA_REGISTER_DIRECT),
OP_SIZE_WL, OP_SIZE_WL,
modInfo = RWM_READ_OP1_OPSIZE or RWM_SET_OP2_OPSIZE modInfo = RWM_READ_OP1_OPSIZE or RWM_SET_OP2_L
), ),
// according to Yann, specifying the registers as bitmask is also valid // according to Yann, specifying the registers as bitmask is also valid
AllowedAdrMode( AllowedAdrMode(
@@ -332,7 +382,7 @@ object M68kIsa {
IsaData( IsaData(
"moveq", "Move Quick", "moveq", "Move Quick",
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), DREG_ONLY, OP_SIZE_L, modInfo = RWM_SET_OP2_L)) modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), DREG_ONLY, OP_SIZE_L, modInfo = RWM_SET_OP2_L, affectedCc = cc("-**00")))
), ),
IsaData( IsaData(
@@ -342,7 +392,7 @@ object M68kIsa {
setOf(AddressMode.DATA_REGISTER_DIRECT, AddressMode.ADDRESS_REGISTER_DIRECT), setOf(AddressMode.DATA_REGISTER_DIRECT, AddressMode.ADDRESS_REGISTER_DIRECT),
setOf(AddressMode.DATA_REGISTER_DIRECT, AddressMode.ADDRESS_REGISTER_DIRECT), setOf(AddressMode.DATA_REGISTER_DIRECT, AddressMode.ADDRESS_REGISTER_DIRECT),
OP_SIZE_L, OP_SIZE_L,
modInfo = RWM_SET_OP1_L or RWM_SET_OP2_L modInfo = RWM_MODIFY_OP1_L or RWM_MODIFY_OP2_L // exchanging registers does not set value to a defined state
) )
) )
), ),
@@ -395,31 +445,18 @@ object M68kIsa {
IsaData("add", "Add", modes = ADD_SUB_MODES), IsaData("add", "Add", modes = ADD_SUB_MODES),
IsaData( IsaData(
"adda", "Add Address", altMnemonics = listOf("add"), "adda", "Add Address", altMnemonics = listOf("add"),
modes = listOf( modes = listOf(AllowedAdrMode(ALL_68000_MODES, AREG_ONLY, OP_SIZE_WL, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_L))
AllowedAdrMode(ALL_68000_MODES, AREG_ONLY, OP_SIZE_WL, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_L),
)
), ),
IsaData( IsaData(
"addi", "Add Immediate", altMnemonics = listOf("add"), "addi", "Add Immediate", altMnemonics = listOf("add"),
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, modInfo = RWM_MODIFY_OP2_OPSIZE))
),
IsaData(
"addq", "Add Quick", modes = listOf(
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, modInfo = RWM_MODIFY_OP2_OPSIZE),
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), AREG_ONLY, size = OP_SIZE_WL, modInfo = RWM_MODIFY_OP2_L)
)
),
IsaData(
"addx", "Add with Extend",
modes = listOf( modes = listOf(
AllowedAdrMode(DREG_ONLY, DREG_ONLY, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE),
AllowedAdrMode( AllowedAdrMode(
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC), setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, modInfo = RWM_MODIFY_OP2_OPSIZE, affectedCc = cc("C****")
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC),
modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE
) )
) )
), ),
IsaData("addq", "Add Quick", modes = ADDQ_SUBQ_MODES),
IsaData("addx", "Add with Extend", modes = ADDX_SUBX_MODES),
IsaData("sub", "Subtract", modes = ADD_SUB_MODES), IsaData("sub", "Subtract", modes = ADD_SUB_MODES),
IsaData( IsaData(
@@ -428,49 +465,67 @@ object M68kIsa {
), ),
IsaData( IsaData(
"subi", "Subtract Immediate", altMnemonics = listOf("sub"), "subi", "Subtract Immediate", altMnemonics = listOf("sub"),
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, modInfo = RWM_MODIFY_OP2_OPSIZE)) modes = listOf(
AllowedAdrMode(
setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, modInfo = RWM_MODIFY_OP2_OPSIZE, affectedCc = cc("C****")
)
)
), ),
IsaData("subq", "Subtract Quick", modes = ADDQ_SUBQ_MODES),
IsaData("subx", "Subtract with Extend", modes = ADDX_SUBX_MODES),
IsaData( IsaData(
"subq", "Subtract Quick", modes = listOf( "neg", "Negate", modes = listOf(
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, modInfo = RWM_MODIFY_OP2_OPSIZE), AllowedAdrMode(
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), AREG_ONLY, size = OP_SIZE_WL, modInfo = RWM_MODIFY_OP2_L) ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, modInfo = RWM_MODIFY_OP1_OPSIZE,
affectedCc = cc("C****")
)
) )
), ),
IsaData( IsaData(
"subx", "Subtract with Extend", "negx", "Negate with Extend", modes = listOf(
modes = listOf(
AllowedAdrMode(DREG_ONLY, DREG_ONLY, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE),
AllowedAdrMode( AllowedAdrMode(
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, modInfo = RWM_MODIFY_OP1_OPSIZE,
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC), affectedCc = cc("C****"), testedCc = cc("?----")
modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE
) )
) )
), ),
IsaData("neg", "Negate", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, modInfo = RWM_MODIFY_OP1_OPSIZE))), IsaData(
IsaData("negx", "Negate with Extend", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, modInfo = RWM_MODIFY_OP1_OPSIZE))), "clr",
"Clear",
IsaData("clr", "Clear", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, modInfo = RWM_SET_OP1_OPSIZE))), modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, modInfo = RWM_SET_OP1_OPSIZE, affectedCc = cc("-0100")))
),
IsaData( IsaData(
"cmp", "Compare", modes = listOf( "cmp", "Compare", modes = listOf(
AllowedAdrMode(ALL_EXCEPT_AREG, setOf(AddressMode.DATA_REGISTER_DIRECT), modInfo = RWM_READ_OP1_OPSIZE or RWM_READ_OP2_OPSIZE), AllowedAdrMode(
ALL_EXCEPT_AREG,
setOf(AddressMode.DATA_REGISTER_DIRECT),
modInfo = RWM_READ_OP1_OPSIZE or RWM_READ_OP2_OPSIZE,
affectedCc = cc("-****")
),
AllowedAdrMode( AllowedAdrMode(
setOf(AddressMode.ADDRESS_REGISTER_DIRECT), setOf(AddressMode.ADDRESS_REGISTER_DIRECT),
setOf(AddressMode.DATA_REGISTER_DIRECT), setOf(AddressMode.DATA_REGISTER_DIRECT),
OP_SIZE_WL, OP_SIZE_WL,
modInfo = RWM_READ_OP1_OPSIZE or RWM_READ_OP2_OPSIZE modInfo = RWM_READ_OP1_OPSIZE or RWM_READ_OP2_OPSIZE,
affectedCc = cc("-****")
), ),
) )
), ),
IsaData( IsaData(
"cmpa", "Compare Address", altMnemonics = listOf("cmp"), "cmpa", "Compare Address", altMnemonics = listOf("cmp"),
modes = listOf(AllowedAdrMode(ALL_68000_MODES, AREG_ONLY, OP_SIZE_WL, modInfo = RWM_READ_OP1_OPSIZE or RWM_READ_OP2_L)) modes = listOf(AllowedAdrMode(ALL_68000_MODES, AREG_ONLY, OP_SIZE_WL, modInfo = RWM_READ_OP1_OPSIZE or RWM_READ_OP2_L, affectedCc = cc("-****")))
), ),
IsaData( IsaData(
"cmpi", "Compare Immediate", altMnemonics = listOf("cmp"), "cmpi", "Compare Immediate", altMnemonics = listOf("cmp"),
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_AND_IMMEDIATE, modInfo = RWM_READ_OP2_OPSIZE)) modes = listOf(
AllowedAdrMode(
setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_AND_IMMEDIATE, modInfo = RWM_READ_OP2_OPSIZE,
affectedCc = cc("-****")
)
)
), ),
IsaData( IsaData(
"cmpm", "Compare Memory to Memory", altMnemonics = listOf("cmp"), "cmpm", "Compare Memory to Memory", altMnemonics = listOf("cmp"),
@@ -478,76 +533,110 @@ object M68kIsa {
AllowedAdrMode( AllowedAdrMode(
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC), setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC),
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC), setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC),
modInfo = RWM_READ_OP1_OPSIZE or RWM_READ_OP2_OPSIZE modInfo = RWM_READ_OP1_OPSIZE or RWM_READ_OP2_OPSIZE,
affectedCc = cc("-****")
) )
) )
), ),
IsaData( IsaData(
"muls", "Signed Multiply", "muls", "Signed Multiply",
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W, modInfo = RWM_READ_OP1_W or RWM_MODIFY_OP2_L)) modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W, modInfo = RWM_READ_OP1_W or RWM_MODIFY_OP2_L, affectedCc = cc("-**00")))
), ),
IsaData( IsaData(
"mulu", "Unsigned Multiply", "mulu", "Unsigned Multiply",
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W, modInfo = RWM_READ_OP1_W or RWM_MODIFY_OP2_L)) modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W, modInfo = RWM_READ_OP1_W or RWM_MODIFY_OP2_L, affectedCc = cc("-**00")))
), ),
IsaData( IsaData(
"divs", "Signed Divide", "divs", "Signed Divide",
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W, modInfo = RWM_READ_OP1_L or RWM_MODIFY_OP2_L)) modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W, modInfo = RWM_READ_OP1_L or RWM_MODIFY_OP2_L, affectedCc = cc("-***0")))
), ),
IsaData( IsaData(
"divu", "Unsigned Divide", "divu", "Unsigned Divide",
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W, modInfo = RWM_READ_OP1_L or RWM_MODIFY_OP2_L)) modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W, modInfo = RWM_READ_OP1_L or RWM_MODIFY_OP2_L, affectedCc = cc("-***0")))
), ),
IsaData("ext", "Sign Extend", modes = listOf(AllowedAdrMode(DREG_ONLY, null, OP_SIZE_WL, modInfo = RWM_MODIFY_OP1_OPSIZE))), IsaData("ext", "Sign Extend", modes = listOf(AllowedAdrMode(DREG_ONLY, null, OP_SIZE_WL, modInfo = RWM_MODIFY_OP1_OPSIZE, affectedCc = cc("-**00")))),
// Logical Instructions // Logical Instructions
IsaData( IsaData(
"and", "Logical AND", "and", "Logical AND",
modes = listOf( modes = listOf(
AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE), AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE, affectedCc = cc("-**00")),
AllowedAdrMode(DREG_ONLY, INDIRECT_MODES, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE) AllowedAdrMode(DREG_ONLY, INDIRECT_MODES, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE, affectedCc = cc("-**00"))
) )
), ),
IsaData( IsaData(
"andi", "Logical AND Immediate", altMnemonics = listOf("and"), "andi", "Logical AND Immediate",
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, modInfo = RWM_MODIFY_OP2_OPSIZE)) altMnemonics = listOf("and"),
modes = listOf(
AllowedAdrMode(
setOf(AddressMode.IMMEDIATE_DATA),
ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL,
modInfo = RWM_MODIFY_OP2_OPSIZE,
affectedCc = cc("-**00")
)
)
), ),
IsaData( IsaData(
"eor", "Logical Exclusive-OR", "eor", "Logical Exclusive-OR",
modes = listOf(AllowedAdrMode(DREG_ONLY, ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE)) modes = listOf(
AllowedAdrMode(
DREG_ONLY,
ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL,
modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE,
affectedCc = cc("-**00")
)
)
), ),
IsaData( IsaData(
"eori", "Logical Exclusive-OR Immediate", altMnemonics = listOf("eor"), "eori", "Logical Exclusive-OR Immediate", altMnemonics = listOf("eor"),
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, modInfo = RWM_MODIFY_OP2_OPSIZE)) modes = listOf(
AllowedAdrMode(
setOf(AddressMode.IMMEDIATE_DATA),
ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL,
modInfo = RWM_MODIFY_OP2_OPSIZE,
affectedCc = cc("-**00")
)
)
), ),
IsaData( IsaData(
"or", "Logical Inclusive-OR", "or", "Logical Inclusive-OR",
modes = listOf( modes = listOf(
AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE), AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE, affectedCc = cc("-**00")),
AllowedAdrMode(DREG_ONLY, INDIRECT_MODES, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE) AllowedAdrMode(DREG_ONLY, INDIRECT_MODES, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE, affectedCc = cc("-**00"))
) )
), ),
IsaData( IsaData(
"ori", "Logical Inclusive-OR Immediate", altMnemonics = listOf("or"), "ori", "Logical Inclusive-OR Immediate", altMnemonics = listOf("or"),
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, modInfo = RWM_MODIFY_OP2_OPSIZE)) modes = listOf(
AllowedAdrMode(
setOf(AddressMode.IMMEDIATE_DATA),
ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL,
modInfo = RWM_MODIFY_OP2_OPSIZE,
affectedCc = cc("-**00")
)
)
), ),
IsaData( IsaData(
"not", "Logical Complement", "not", "Logical Complement",
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, modInfo = RWM_MODIFY_OP1_OPSIZE)) modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, modInfo = RWM_MODIFY_OP1_OPSIZE, affectedCc = cc("-**00")))
), ),
// Shift and Rotate Instructions // Shift and Rotate Instructions
IsaData("asl", "Arithmetic Shift Left", modes = ASD_LSD_ROD_ROXD_MODES), IsaData("asl", "Arithmetic Shift Left", modes = ASD_LSD_MODES),
IsaData("asr", "Arithmetic Shift Right", modes = ASD_LSD_ROD_ROXD_MODES), IsaData("asr", "Arithmetic Shift Right", modes = ASD_LSD_MODES),
IsaData("lsl", "Logical Shift Left", modes = ASD_LSD_ROD_ROXD_MODES), IsaData("lsl", "Logical Shift Left", modes = ASD_LSD_MODES),
IsaData("lsr", "Logical Shift Right", modes = ASD_LSD_ROD_ROXD_MODES), IsaData("lsr", "Logical Shift Right", modes = ASD_LSD_MODES),
IsaData("rol", "Rotate Left", modes = ASD_LSD_ROD_ROXD_MODES), IsaData("rol", "Rotate Left", modes = ROD_MODES),
IsaData("ror", "Rotate Right", modes = ASD_LSD_ROD_ROXD_MODES), IsaData("ror", "Rotate Right", modes = ROD_MODES),
IsaData("roxl", "Rotate with Extend Left", modes = ASD_LSD_ROD_ROXD_MODES), IsaData("roxl", "Rotate with Extend Left", modes = ROXD_MODES),
IsaData("roxr", "Rotate with Extend Right", modes = ASD_LSD_ROD_ROXD_MODES), IsaData("roxr", "Rotate with Extend Right", modes = ROXD_MODES),
IsaData("swap", "Swap Register Words", modes = listOf(AllowedAdrMode(DREG_ONLY, null, OP_SIZE_W, modInfo = RWM_MODIFY_OP1_L))), IsaData(
"swap",
"Swap Register Words",
modes = listOf(AllowedAdrMode(DREG_ONLY, null, OP_SIZE_W, modInfo = RWM_MODIFY_OP1_L, affectedCc = cc("-**00")))
),
// Bit Manipulation Instructions // Bit Manipulation Instructions
IsaData("bchg", "Test Bit and Change", modes = BCHG_BCLR_BSET_MODES), IsaData("bchg", "Test Bit and Change", modes = BCHG_BCLR_BSET_MODES),
@@ -559,42 +648,61 @@ object M68kIsa {
IsaData( IsaData(
"abcd", "Add Decimal with Extend", "abcd", "Add Decimal with Extend",
modes = listOf( modes = listOf(
AllowedAdrMode(DREG_ONLY, DREG_ONLY, OP_SIZE_B, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE), AllowedAdrMode(
DREG_ONLY, DREG_ONLY, OP_SIZE_B, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE,
affectedCc = cc("CUAU*"), testedCc = cc("?-?--")
),
AllowedAdrMode( AllowedAdrMode(
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC), setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC),
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC), setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC),
OP_SIZE_B, OP_SIZE_B,
modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE,
affectedCc = cc("CUAU*"), testedCc = cc("?-?--")
) )
) )
), ),
IsaData( IsaData(
"sbcd", "Subtract Decimal with Extend", "sbcd", "Subtract Decimal with Extend",
modes = listOf( modes = listOf(
AllowedAdrMode(DREG_ONLY, DREG_ONLY, OP_SIZE_B, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE), AllowedAdrMode(
DREG_ONLY, DREG_ONLY, OP_SIZE_B, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE,
affectedCc = cc("CUAU*"), testedCc = cc("?-?--")
),
AllowedAdrMode( AllowedAdrMode(
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC), setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC),
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC), setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC),
OP_SIZE_B, OP_SIZE_B,
modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE,
affectedCc = cc("CUAU*"), testedCc = cc("?-?--")
) )
) )
), ),
IsaData( IsaData(
"nbcd", "Negate Decimal with Extend", "nbcd", "Negate Decimal with Extend",
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, OP_SIZE_B, modInfo = RWM_MODIFY_OP1_OPSIZE)) modes = listOf(
AllowedAdrMode(
ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, OP_SIZE_B, modInfo = RWM_MODIFY_OP1_OPSIZE,
affectedCc = cc("CUAU*"), testedCc = cc("?-?--")
)
)
), ),
// Program Control Instructions // Program Control Instructions
IsaData( IsaData(
"bCC", "Branch Conditionally", conditionCodes = conditionCodesBcc, "bCC", "Branch Conditionally", conditionCodes = conditionCodesBcc,
modes = listOf(AllowedAdrMode(setOf(AddressMode.ABSOLUTE_ADDRESS), null, OP_SIZE_SBW)) modes = listOf(AllowedAdrMode(setOf(AddressMode.ABSOLUTE_ADDRESS), null, OP_SIZE_SBW, testedCc = cc("-????"))),
changesControlFlow = true
),
IsaData(
"bra", "Branch",
modes = listOf(AllowedAdrMode(setOf(AddressMode.ABSOLUTE_ADDRESS), null, OP_SIZE_SBW)),
changesControlFlow = true
), ),
IsaData("bra", "Branch", modes = listOf(AllowedAdrMode(setOf(AddressMode.ABSOLUTE_ADDRESS), null, OP_SIZE_SBW))),
IsaData( IsaData(
"bsr", "bsr",
"Branch to Subroutine", "Branch to Subroutine",
modes = listOf(AllowedAdrMode(setOf(AddressMode.ABSOLUTE_ADDRESS), null, OP_SIZE_SBW, modInfo = RWM_MODIFY_STACK)) modes = listOf(AllowedAdrMode(setOf(AddressMode.ABSOLUTE_ADDRESS), null, OP_SIZE_SBW, modInfo = RWM_MODIFY_STACK)),
changesControlFlow = true
), ),
IsaData( IsaData(
@@ -602,11 +710,12 @@ object M68kIsa {
"Test Condition, Decrement, and Branch", "Test Condition, Decrement, and Branch",
altMnemonics = listOf("dbra"), altMnemonics = listOf("dbra"),
conditionCodes = conditionCodes, conditionCodes = conditionCodes,
modes = listOf(AllowedAdrMode(DREG_ONLY, setOf(AddressMode.ABSOLUTE_ADDRESS), OP_SIZE_W, modInfo = RWM_MODIFY_OP1_W)) modes = listOf(AllowedAdrMode(DREG_ONLY, setOf(AddressMode.ABSOLUTE_ADDRESS), OP_SIZE_W, modInfo = RWM_MODIFY_OP1_W, testedCc = cc("-????"))),
changesControlFlow = true
), ),
IsaData( IsaData(
"sCC", "Set Conditionally", conditionCodes = conditionCodes, "sCC", "Set Conditionally", conditionCodes = conditionCodes,
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, OP_SIZE_B, modInfo = RWM_SET_OP2_B)) modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, OP_SIZE_B, modInfo = RWM_SET_OP2_B, testedCc = cc("-????")))
), ),
IsaData( IsaData(
@@ -622,7 +731,8 @@ object M68kIsa {
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX
), null, OP_UNSIZED ), null, OP_UNSIZED
) )
) ),
changesControlFlow = true
), ),
IsaData( IsaData(
"jsr", "Jump to Subroutine", "jsr", "Jump to Subroutine",
@@ -637,27 +747,60 @@ object M68kIsa {
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX
), null, OP_UNSIZED, modInfo = RWM_MODIFY_STACK ), null, OP_UNSIZED, modInfo = RWM_MODIFY_STACK
) )
) ),
changesControlFlow = true
), ),
IsaData("nop", "No Operation", hasOps = false, modes = NO_OPS_UNSIZED), IsaData("nop", "No Operation", hasOps = false, modes = NO_OPS_UNSIZED),
IsaData("rtr", "Return and Restore", hasOps = false, modes = listOf(AllowedAdrMode(size = OP_UNSIZED, modInfo = RWM_MODIFY_STACK))), IsaData(
IsaData("rts", "Return from Subroutine", hasOps = false, modes = listOf(AllowedAdrMode(size = OP_UNSIZED, modInfo = RWM_MODIFY_STACK))), "rtr",
"Return and Restore",
hasOps = false,
modes = listOf(AllowedAdrMode(size = OP_UNSIZED, modInfo = RWM_MODIFY_STACK, affectedCc = cc("*****"))),
changesControlFlow = true
),
IsaData(
"rts", "Return from Subroutine", hasOps = false,
modes = listOf(AllowedAdrMode(size = OP_UNSIZED, modInfo = RWM_MODIFY_STACK)),
changesControlFlow = true
),
IsaData("tst", "Test Operand", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, modInfo = RWM_READ_OP1_OPSIZE))), IsaData(
"tst", "Test Operand", modes = listOf(
AllowedAdrMode(
ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, modInfo = RWM_READ_OP1_OPSIZE,
affectedCc = cc("-**00")
)
)
),
// System Control Instructions // System Control Instructions
IsaData( IsaData(
"andi", "AND Immediate to Status Register", id = "andi to SR", altMnemonics = listOf("and"), isPrivileged = true, "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")) modes = listOf(
AllowedAdrMode(
setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr",
affectedCc = cc("AAAAA"), testedCc = cc("?????")
)
)
), ),
IsaData( IsaData(
"eori", "Exclusive-OR Immediate to Status Register", id = "eori to SR", altMnemonics = listOf("eor"), isPrivileged = true, "eori", "Exclusive-OR Immediate to Status Register", id = "eori to SR", altMnemonics = listOf("eor"), isPrivileged = true,
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr")) modes = listOf(
AllowedAdrMode(
setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr",
affectedCc = cc("*****"), testedCc = cc("?????")
)
)
), ),
IsaData( IsaData(
"ori", "Inclusive-OR Immediate to Status Register", id = "ori to SR", altMnemonics = listOf("or"), isPrivileged = true, "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")) modes = listOf(
AllowedAdrMode(
setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr",
affectedCc = cc("OOOOO"), testedCc = cc("?????")
)
)
), ),
IsaData( IsaData(
"move", "Move from Status Register", id = "move from SR", "move", "Move from Status Register", id = "move from SR",
@@ -667,13 +810,19 @@ object M68kIsa {
ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL,
OP_SIZE_W, OP_SIZE_W,
"sr", "sr",
modInfo = RWM_SET_OP2_W modInfo = RWM_SET_OP2_W,
testedCc = cc("?????")
) )
) )
), ),
IsaData( IsaData(
"move", "Move to Status Register", id = "move to SR", isPrivileged = true, "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")) modes = listOf(
AllowedAdrMode(
ALL_EXCEPT_AREG, setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr",
affectedCc = cc("*****")
)
)
), ),
IsaData( IsaData(
"move", "Move User Stack Pointer", id = "move USP", isPrivileged = true, "move", "Move User Stack Pointer", id = "move USP", isPrivileged = true,
@@ -686,7 +835,8 @@ object M68kIsa {
IsaData("reset", "Reset External Devices", isPrivileged = true, hasOps = false, modes = NO_OPS_UNSIZED), IsaData("reset", "Reset External Devices", isPrivileged = true, hasOps = false, modes = NO_OPS_UNSIZED),
IsaData( IsaData(
"rte", "Return from Exception", isPrivileged = true, hasOps = false, "rte", "Return from Exception", isPrivileged = true, hasOps = false,
modes = listOf(AllowedAdrMode(size = OP_UNSIZED, modInfo = RWM_MODIFY_STACK)) modes = listOf(AllowedAdrMode(size = OP_UNSIZED, modInfo = RWM_MODIFY_STACK)),
changesControlFlow = true
), ),
IsaData( IsaData(
"stop", "Stop", isPrivileged = true, "stop", "Stop", isPrivileged = true,
@@ -695,59 +845,70 @@ object M68kIsa {
IsaData( IsaData(
"chk", "Check Register Against Bound", "chk", "Check Register Against Bound",
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W, modInfo = RWM_READ_OP1_W or RWM_READ_OP2_W)) modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, OP_SIZE_W, modInfo = RWM_READ_OP1_W or RWM_READ_OP2_W, affectedCc = cc("-*UUU")))
),
IsaData("illegal", "Take Illegal Instruction Trap", hasOps = false, modes = NO_OPS_UNSIZED, changesControlFlow = true),
IsaData("trap", "Trap", modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), null, OP_UNSIZED)), changesControlFlow = true),
IsaData(
"trapv", "Trap on Overflow", hasOps = false,
modes = listOf(AllowedAdrMode(size = OP_UNSIZED, testedCc = cc("---?-"))),
changesControlFlow = true
), ),
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( IsaData(
"andi", "AND Immediate to Condition Code Register", id = "andi to CCR", altMnemonics = listOf("and"), "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")) modes = listOf(
AllowedAdrMode(
setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_B, "ccr",
affectedCc = cc("AAAAA"), testedCc = cc("?????")
)
)
), ),
IsaData( IsaData(
"eori", "Exclusive-OR Immediate to Condition Code Register", id = "eori to CCR", altMnemonics = listOf("eor"), "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")) modes = listOf(
AllowedAdrMode(
setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_B, "ccr",
affectedCc = cc("*****"), testedCc = cc("?????")
)
)
), ),
IsaData( IsaData(
"ori", "Inclusive-OR Immediate to Condition Code Register", id = "ori to CCR", altMnemonics = listOf("or"), "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")) modes = listOf(
AllowedAdrMode(
setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_B, "ccr",
affectedCc = cc("OOOOO"), testedCc = cc("?????")
)
)
), ),
IsaData( IsaData(
"move", "Move to Condition Code Register", id = "move to CCR", "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")) modes = listOf(
AllowedAdrMode(ALL_EXCEPT_AREG, setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "ccr", affectedCc = cc("*****")),
)
), ),
// Multiprocessor Instructions // Multiprocessor Instructions
IsaData( IsaData(
"tas", "Test Operand and Set", "tas", "Test Operand and Set",
modes = listOf(AllowedAdrMode(ALL_EXCEPT_IMMEDIATE_AND_PC_REL, null, OP_SIZE_B, modInfo = RWM_MODIFY_OP1_B)) modes = listOf(AllowedAdrMode(ALL_EXCEPT_IMMEDIATE_AND_PC_REL, null, OP_SIZE_B, modInfo = RWM_MODIFY_OP1_B, affectedCc = cc("-**00")))
) )
) )
val mnemonics = private val mnemonicLookupMap = isaData.asSequence()
isaData.asSequence() .flatMap {
.flatMap { (if (it.conditionCodes.isEmpty()) it.altMnemonics.plus(it.mnemonic) else it.altMnemonics.plus(it.conditionCodes
if (it.conditionCodes.isEmpty()) it.altMnemonics.plus(it.mnemonic) else it.altMnemonics.plus(it.conditionCodes .map { cc ->
.map { cc -> it.mnemonic.replace("CC", cc)
it.mnemonic.replace("CC", cc) })).map { mnemonic -> mnemonic to it }
}) }
} .groupBy({ it.first }) { it.second }
.toSet()
val mnemonics = mnemonicLookupMap.keys
fun findMatchingInstructions(mnemonic: String): List<IsaData> { fun findMatchingInstructions(mnemonic: String): List<IsaData> {
val lowerMnemonic = mnemonic.lowercase() return mnemonicLookupMap.getOrDefault(mnemonic.lowercase(), emptyList())
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> { fun findMatchingOpMode(candidates: List<IsaData>, op1: AddressMode?, op2: AddressMode?, opSize: Int?, specialReg: String?): List<IsaData> {
@@ -10,6 +10,7 @@ import com.intellij.psi.PsiFile
import de.platon42.intellij.plugins.m68k.asm.AddressMode import de.platon42.intellij.plugins.m68k.asm.AddressMode
import de.platon42.intellij.plugins.m68k.asm.IsaData import de.platon42.intellij.plugins.m68k.asm.IsaData
import de.platon42.intellij.plugins.m68k.asm.M68kIsa import de.platon42.intellij.plugins.m68k.asm.M68kIsa
import de.platon42.intellij.plugins.m68k.asm.getCcInfo
import de.platon42.intellij.plugins.m68k.psi.M68kAsmInstruction import de.platon42.intellij.plugins.m68k.psi.M68kAsmInstruction
import de.platon42.intellij.plugins.m68k.psi.M68kAsmOp import de.platon42.intellij.plugins.m68k.psi.M68kAsmOp
import de.platon42.intellij.plugins.m68k.psi.M68kOperandSize import de.platon42.intellij.plugins.m68k.psi.M68kOperandSize
@@ -36,24 +37,67 @@ class M68kInstructionDocumentationProvider : AbstractDocumentationProvider() {
val defBuilder = createDefinition(isaData) val defBuilder = createDefinition(isaData)
builder.append(defBuilder.wrapWith(DocumentationMarkup.DEFINITION_ELEMENT)) builder.append(defBuilder.wrapWith(DocumentationMarkup.DEFINITION_ELEMENT))
val hasSameCcsForEverything = isaData.modes.map { it.affectedCc }.distinct().count() == 1
var alreadyShownCcsOnce = false
val mnemonicInfoRows = HtmlBuilder() val mnemonicInfoRows = HtmlBuilder()
val headerCells = listOf( mnemonicInfoRows.appendWithSeparators(HtmlChunk.tag("tr").child(HtmlChunk.hr().wrapWith(DocumentationMarkup.SECTION_CONTENT_CELL).attr("colspan", "3")),
HtmlChunk.text("Mnemonic").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL), isaData.modes.map { allowedAdrMode ->
HtmlChunk.text("Op1").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL), val addressModeInfoRows = HtmlBuilder()
HtmlChunk.text("Op2").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL) val headerCells = if (allowedAdrMode.op2 != null) {
) listOf(
mnemonicInfoRows.append(HtmlChunk.tag("tr").children(headerCells)) HtmlChunk.text("Mnemonic / CCs").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL),
isaData.modes.forEach { allowedAdrMode -> HtmlChunk.text("Operand 1").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL),
val mnemonics = findOpSizeDescriptions(allowedAdrMode.size) HtmlChunk.text("Operand 2").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL)
.map { HtmlChunk.text(isaData.mnemonic + it).wrapWith(HtmlChunk.div()) } )
mnemonicInfoRows.append( } else if (allowedAdrMode.op1 != null) {
HtmlChunk.tag("tr").children( listOf(
DocumentationMarkup.SECTION_CONTENT_CELL.children(mnemonics), HtmlChunk.text("Mnemonic / CCs").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL),
DocumentationMarkup.SECTION_CONTENT_CELL.child(collectAddressModes(allowedAdrMode.op1)), HtmlChunk.text("Operand").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL)
DocumentationMarkup.SECTION_CONTENT_CELL.child(collectAddressModes(allowedAdrMode.op2)) )
) } else {
) listOf(HtmlChunk.text("Mnemonic / CCs").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL))
} }
addressModeInfoRows.append(HtmlChunk.tag("tr").children(headerCells))
val contentBuilder = HtmlBuilder()
val mnemonics = findOpSizeDescriptions(allowedAdrMode.size)
.map { HtmlChunk.text(isaData.mnemonic + it) }
contentBuilder.appendWithSeparators(HtmlChunk.br(), mnemonics)
contentBuilder.append(HtmlChunk.hr())
if (alreadyShownCcsOnce && hasSameCcsForEverything) {
contentBuilder.append(HtmlChunk.text("Condition Codes: Same as above"))
} else {
alreadyShownCcsOnce = true
contentBuilder.append(HtmlChunk.text("Condition Codes: "))
contentBuilder.append(HtmlChunk.br())
if (allowedAdrMode.affectedCc == 0) {
contentBuilder.append(HtmlChunk.text("Not affected."))
} else {
val ccMap = getCcInfo(allowedAdrMode.affectedCc)
val ccShortTableRows = HtmlBuilder()
ccShortTableRows.append(
HtmlChunk.tag("tr").children(ccMap.keys.map { HtmlChunk.text(it).wrapWith(DocumentationMarkup.SECTION_HEADER_CELL) })
)
ccShortTableRows.append(
HtmlChunk.tag("tr").children(ccMap.values.map {
HtmlChunk.text(it.first).wrapWith(DocumentationMarkup.SECTION_CONTENT_CELL)
})
)
contentBuilder.append(ccShortTableRows.wrapWith(DocumentationMarkup.SECTIONS_TABLE))
contentBuilder.appendWithSeparators(HtmlChunk.br(), ccMap.map {
HtmlChunk.text(it.key + " - " + it.value.second)
})
}
}
val cellsPerRow = ArrayList<HtmlChunk>(3)
cellsPerRow.add(contentBuilder.toFragment())
if (allowedAdrMode.op1 != null) cellsPerRow.add(collectAddressModes(allowedAdrMode.op1))
if (allowedAdrMode.op1 != null) cellsPerRow.add(collectAddressModes(allowedAdrMode.op2))
addressModeInfoRows.append(HtmlChunk.tag("tr").children(cellsPerRow.map { it.wrapWith(DocumentationMarkup.SECTION_CONTENT_CELL) }))
addressModeInfoRows.toFragment()
})
val contentBuilder = HtmlBuilder() val contentBuilder = HtmlBuilder()
contentBuilder.append(mnemonicInfoRows.wrapWith(DocumentationMarkup.SECTIONS_TABLE)) contentBuilder.append(mnemonicInfoRows.wrapWith(DocumentationMarkup.SECTIONS_TABLE))
@@ -13,8 +13,8 @@ import com.intellij.ui.JBColor
import de.platon42.intellij.plugins.m68k.asm.* import de.platon42.intellij.plugins.m68k.asm.*
import de.platon42.intellij.plugins.m68k.asm.Register.Companion.getRegFromName import de.platon42.intellij.plugins.m68k.asm.Register.Companion.getRegFromName
import de.platon42.intellij.plugins.m68k.psi.* import de.platon42.intellij.plugins.m68k.psi.*
import de.platon42.intellij.plugins.m68k.psi.M68kAddressModeUtil.getOtherReadWriteModifyRegisters import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.checkIfInstructionUsesRegister
import de.platon42.intellij.plugins.m68k.psi.M68kAddressModeUtil.getReadWriteModifyRegisters import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.evaluateRegisterUse
import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.findExactIsaDataAndAllowedAdrModeForInstruction import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.findExactIsaDataAndAllowedAdrModeForInstruction
import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.getOpSizeOrDefault import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.getOpSizeOrDefault
import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.modifyRwmWithOpsize import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.modifyRwmWithOpsize
@@ -52,7 +52,7 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
val firstOp = asmInstruction.addressingModeList[0] == addressingMode val firstOp = asmInstruction.addressingModeList[0] == addressingMode
val cursorRwm = modifyRwmWithOpsize((adrMode.modInfo ushr if (firstOp) RWM_OP1_SHIFT else RWM_OP2_SHIFT) and RWM_OP_MASK, opSize) val cursorRwm = modifyRwmWithOpsize((adrMode.modInfo ushr if (firstOp) RWM_OP1_SHIFT else RWM_OP2_SHIFT) and RWM_OP_MASK, opSize)
val backtrace: MutableList<HtmlChunk> = ArrayList() val backtrace = ArrayList<HtmlChunk>()
val missingBits = if (cursorRwm and RWM_SET_L != 0) { val missingBits = if (cursorRwm and RWM_SET_L != 0) {
if (totalRwm and RWM_SET_L == RWM_SET_L) { if (totalRwm and RWM_SET_L == RWM_SET_L) {
backtrace.add( backtrace.add(
@@ -64,10 +64,10 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
) )
0 0
} else { } else {
(RWM_SET_L and RWM_SIZE_MASK) and totalRwm.inv() RWM_SIZE_MASK and totalRwm.inv()
} }
} else { } else {
(RWM_SET_L and RWM_SIZE_MASK) and ((cursorRwm and RWM_MODIFY_L) ushr 8) RWM_SIZE_MASK and (((cursorRwm and RWM_MODIFY_L) ushr RWM_MODIFY_SHIFT) or ((cursorRwm and RWM_READ_L) ushr RWM_READ_SHIFT))
} }
val initialStatement: M68kStatement = asmInstruction.parent as M68kStatement val initialStatement: M68kStatement = asmInstruction.parent as M68kStatement
val localLabelName = PsiTreeUtil.findChildOfType(initialStatement, M68kLocalLabel::class.java)?.name ?: "-->" val localLabelName = PsiTreeUtil.findChildOfType(initialStatement, M68kLocalLabel::class.java)?.name ?: "-->"
@@ -85,7 +85,7 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
) )
}) })
backtrace.reverse() backtrace.reverse()
val traceBits = (cursorRwm or (cursorRwm ushr 8)) and RWM_SIZE_MASK val traceBits = (cursorRwm or (cursorRwm ushr RWM_MODIFY_SHIFT) or (cursorRwm ushr RWM_READ_SHIFT)) and RWM_SIZE_MASK
backtrace.addAll(analyseFlow(register, traceBits, false, initialStatement, linesLimit) { backtrace.addAll(analyseFlow(register, traceBits, false, initialStatement, linesLimit) {
PsiTreeUtil.getNextSiblingOfType( PsiTreeUtil.getNextSiblingOfType(
it, it,
@@ -108,10 +108,10 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
startingStatement: M68kStatement, startingStatement: M68kStatement,
linesLimit: Int, linesLimit: Int,
direction: (statement: M68kStatement) -> M68kStatement? direction: (statement: M68kStatement) -> M68kStatement?
): MutableList<HtmlChunk> { ): List<HtmlChunk> {
var missingBits = rwmBits var missingBits = rwmBits
var currStatement = startingStatement var currStatement = startingStatement
val statementLines: MutableList<HtmlChunk> = ArrayList() val statementLines = ArrayList<HtmlChunk>()
val rn = register.regname val rn = register.regname
var addAbrevDots = false var addAbrevDots = false
var lines = 0 var lines = 0
@@ -189,12 +189,12 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
private fun rwmToDisplayText(rwm: Int, rn: String) = private fun rwmToDisplayText(rwm: Int, rn: String) =
when (rwm) { when (rwm) {
RWM_READ_B -> "reads $rn.b" RWM_READ_B -> "uses $rn.b"
RWM_READ_W -> "reads $rn.w" RWM_READ_W -> "uses $rn.w"
RWM_READ_L -> "reads $rn.l" RWM_READ_L -> "uses $rn.l"
RWM_MODIFY_B -> "modifies $rn.b" RWM_MODIFY_B -> "changes $rn.b"
RWM_MODIFY_W -> "modifies $rn.w" RWM_MODIFY_W -> "changes $rn.w"
RWM_MODIFY_L -> "modifies $rn.l" RWM_MODIFY_L -> "changes $rn.l"
RWM_SET_B -> "sets $rn.b" RWM_SET_B -> "sets $rn.b"
RWM_SET_W -> "sets $rn.w" RWM_SET_W -> "sets $rn.w"
RWM_SET_L -> "sets $rn.l" RWM_SET_L -> "sets $rn.l"
@@ -210,30 +210,6 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
) )
.children(DocumentationMarkup.SECTION_CONTENT_CELL.child(HtmlChunk.nbsp())) .children(DocumentationMarkup.SECTION_CONTENT_CELL.child(HtmlChunk.nbsp()))
private fun evaluateRegisterUse(
asmInstruction: M68kAsmInstruction,
adrMode: AllowedAdrMode,
register: Register
): List<Int> {
val opSize = getOpSizeOrDefault(asmInstruction.asmOp.opSize, adrMode)
val rwm1 = modifyRwmWithOpsize((adrMode.modInfo ushr RWM_OP1_SHIFT) and RWM_OP_MASK, opSize)
val rwm2 = if (asmInstruction.addressingModeList.size > 1) modifyRwmWithOpsize((adrMode.modInfo ushr RWM_OP2_SHIFT) and RWM_OP_MASK, opSize) else 0
return getReadWriteModifyRegisters(asmInstruction.addressingModeList[0], rwm1).asSequence()
.plus(getReadWriteModifyRegisters(asmInstruction.addressingModeList.getOrNull(1), rwm2))
.plus(getOtherReadWriteModifyRegisters(adrMode.modInfo))
.filter { it.first == register }
.map { it.second }
.toList()
}
private fun checkIfInstructionUsesRegister(instruction: M68kAsmInstruction, register: Register): Boolean {
if (instruction.addressingModeList.isEmpty()) {
return false
}
return instruction.addressingModeList.any { aml -> getReadWriteModifyRegisters(aml, 0).any { it.first == register } }
}
override fun getCustomDocumentationElement(editor: Editor, file: PsiFile, contextElement: PsiElement?, targetOffset: Int): PsiElement? { override fun getCustomDocumentationElement(editor: Editor, file: PsiFile, contextElement: PsiElement?, targetOffset: Int): PsiElement? {
if (contextElement == null) return null if (contextElement == null) return null
if (contextElement is M68kDataRegister || contextElement is M68kAddressRegister) return contextElement if (contextElement is M68kDataRegister || contextElement is M68kAddressRegister) return contextElement
@@ -0,0 +1,104 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.codeInspection.InspectionManager
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.SmartList
import de.platon42.intellij.plugins.m68k.asm.*
import de.platon42.intellij.plugins.m68k.asm.M68kIsa.findMatchingInstructions
import de.platon42.intellij.plugins.m68k.psi.*
import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil
import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.checkIfInstructionUsesRegister
import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.evaluateRegisterUse
import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.findExactIsaDataAndAllowedAdrModeForInstruction
import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.getConcreteTestedCcFromMnemonic
class M68kDeadWriteInspection : AbstractBaseM68kLocalInspectionTool() {
companion object {
private const val DISPLAY_NAME = "Dead writes to registers"
private const val DEAD_WRITE_MSG_TEMPLATE = "Register %s is overwritten later without being used"
private const val POSSIBLY_DEAD_WRITE_MSG_TEMPLATE = "Register %s is overwritten later (only CC evaluated?)"
}
override fun getDisplayName() = DISPLAY_NAME
override fun checkAsmInstruction(asmInstruction: M68kAsmInstruction, manager: InspectionManager, isOnTheFly: Boolean): Array<ProblemDescriptor>? {
val asmOp = asmInstruction.asmOp
if (asmInstruction.addressingModeList.isEmpty()) return emptyArray()
val isaDataCandidates = findMatchingInstructions(asmOp.mnemonic)
if (isaDataCandidates.isEmpty()) return emptyArray()
val (_, adrMode) = findExactIsaDataAndAllowedAdrModeForInstruction(asmInstruction) ?: return emptyArray()
val opSize = M68kIsaUtil.getOpSizeOrDefault(asmInstruction.asmOp.opSize, adrMode)
val rwm1 = M68kIsaUtil.modifyRwmWithOpsize((adrMode.modInfo ushr RWM_OP1_SHIFT) and RWM_OP_MASK, opSize)
val rwm2 = if (asmInstruction.addressingModeList.size > 1) M68kIsaUtil.modifyRwmWithOpsize(
(adrMode.modInfo ushr RWM_OP2_SHIFT) and RWM_OP_MASK, opSize
) else 0
val regsWritten = M68kAddressModeUtil.getReadWriteModifyRegisters(asmInstruction.addressingModeList[0], rwm1).asSequence()
.plus(M68kAddressModeUtil.getReadWriteModifyRegisters(asmInstruction.addressingModeList.getOrNull(1), rwm2))
.plus(M68kAddressModeUtil.getOtherReadWriteModifyRegisters(adrMode.modInfo))
.filter { (it.second and RWM_SET_L) > 0 }
.distinct()
.toList()
val hints = SmartList<ProblemDescriptor>()
for (regPair in regsWritten) {
val register = regPair.first
var rwm = regPair.second
var currStatement = asmInstruction.parent as M68kStatement
var ccModification = adrMode.affectedCc
var ccOverwritten = false
var ccTested = false
var hasModification = false
while (true) {
currStatement = PsiTreeUtil.getNextSiblingOfType(currStatement, M68kStatement::class.java) ?: break
val globalLabel = PsiTreeUtil.findChildOfType(currStatement, M68kGlobalLabel::class.java)
if (globalLabel != null) break
if (PsiTreeUtil.getChildOfType(currStatement, M68kPreprocessorDirective::class.java) != null) break
// as we cannot evaluate macros right now, abort at macros containing the register name (only lower case for simplicity)
val macroCall = PsiTreeUtil.getChildOfType(currStatement, M68kMacroCall::class.java)
if (macroCall?.exprList?.any { it.textMatches(register.regname) } == true) break
val currAsmInstruction = PsiTreeUtil.getChildOfType(currStatement, M68kAsmInstruction::class.java) ?: continue
val (isaData, currAdrMode) = findExactIsaDataAndAllowedAdrModeForInstruction(currAsmInstruction) ?: continue
if (isaData.changesControlFlow) break
val testedCc = getConcreteTestedCcFromMnemonic(currAsmInstruction.asmOp.mnemonic, isaData, currAdrMode)
if (((testedCc and ccModification) > 0) && !ccOverwritten) ccTested = true
if (currAdrMode.affectedCc != 0) ccOverwritten = true
if (checkIfInstructionUsesRegister(currAsmInstruction, register)) {
val totalRwms = evaluateRegisterUse(currAsmInstruction, currAdrMode, register).reduce(Int::or)
if (totalRwms and RWM_READ_L > 0) break
if (totalRwms and RWM_MODIFY_L > 0) {
hasModification = true
ccOverwritten = false
ccModification = ccModification or currAdrMode.affectedCc
rwm = (totalRwms ushr RWM_MODIFY_SHIFT) and RWM_SET_L
}
if (totalRwms and RWM_SET_L >= rwm) {
if (ccTested && hasModification) {
break
}
hints.add(
manager.createProblemDescriptor(
asmInstruction,
asmInstruction,
(if (ccTested) POSSIBLY_DEAD_WRITE_MSG_TEMPLATE else DEAD_WRITE_MSG_TEMPLATE).format(register.regname),
if (ccTested) ProblemHighlightType.WEAK_WARNING else ProblemHighlightType.WARNING,
isOnTheFly
)
)
break
}
}
}
}
return hints.toTypedArray()
}
}
@@ -133,7 +133,6 @@ class M68kSyntaxInspection : AbstractBaseM68kLocalInspectionTool() {
) )
} }
val supportedOpSizes = findSupportedOpSizes(matchingModeIsaDataIgnoringSize, op1, op2, specialReg) val supportedOpSizes = findSupportedOpSizes(matchingModeIsaDataIgnoringSize, op1, op2, specialReg)
return arrayOf( return arrayOf(
when (supportedOpSizes) { when (supportedOpSizes) {
@@ -0,0 +1,54 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.codeInspection.InspectionManager
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.psi.util.PsiTreeUtil
import de.platon42.intellij.plugins.m68k.asm.M68kIsa.findMatchingInstructions
import de.platon42.intellij.plugins.m68k.psi.*
import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil
import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.findExactIsaDataAndAllowedAdrModeForInstruction
class M68kUnexpectedConditionalInstructionInspection : AbstractBaseM68kLocalInspectionTool() {
companion object {
private const val DISPLAY_NAME = "Unaffected condition codes before conditional instruction"
private const val UNAFFECTED_CONDITION_CODES_MSG_TEMPLATE = "Condition codes unaffected by instruction (%s - %s)"
}
override fun getDisplayName() = DISPLAY_NAME
override fun checkAsmInstruction(asmInstruction: M68kAsmInstruction, manager: InspectionManager, isOnTheFly: Boolean): Array<ProblemDescriptor>? {
val asmOp = asmInstruction.asmOp
if (asmInstruction.addressingModeList.isEmpty()) return emptyArray()
val isaDataCandidates = findMatchingInstructions(asmOp.mnemonic)
if (isaDataCandidates.isEmpty()) return emptyArray()
val (isaData, adrMode) = findExactIsaDataAndAllowedAdrModeForInstruction(asmInstruction) ?: return emptyArray()
if ((adrMode.affectedCc > 0) || (adrMode.testedCc > 0) || isaData.changesControlFlow) return emptyArray()
var currStatement = asmInstruction.parent as M68kStatement
while (true) {
currStatement = PsiTreeUtil.getNextSiblingOfType(currStatement, M68kStatement::class.java) ?: break
val globalLabel = PsiTreeUtil.findChildOfType(currStatement, M68kGlobalLabel::class.java)
if (globalLabel != null) break
if (PsiTreeUtil.findChildOfAnyType(currStatement, M68kMacroCall::class.java, M68kPreprocessorDirective::class.java) != null) break
val currAsmInstruction = PsiTreeUtil.getChildOfType(currStatement, M68kAsmInstruction::class.java) ?: continue
val (currIsaData, currAdrMode) = findExactIsaDataAndAllowedAdrModeForInstruction(currAsmInstruction) ?: break
val testedCc = M68kIsaUtil.getConcreteTestedCcFromMnemonic(currAsmInstruction.asmOp.mnemonic, currIsaData, currAdrMode)
if (testedCc == 0) break
return arrayOf(
manager.createProblemDescriptor(
asmInstruction,
asmInstruction,
UNAFFECTED_CONDITION_CODES_MSG_TEMPLATE.format(isaData.mnemonic, isaData.description),
ProblemHighlightType.WARNING,
isOnTheFly
)
)
}
return emptyArray()
}
}
@@ -43,8 +43,8 @@ object M68kAddressModeUtil {
is M68kProgramCounterIndirectWithDisplacementOldAddressingMode, is M68kProgramCounterIndirectWithDisplacementOldAddressingMode,
is M68kAbsoluteAddressAddressingMode -> emptyList() is M68kAbsoluteAddressAddressingMode -> emptyList()
is M68kAddressRegisterIndirectPostIncAddressingMode -> listOf(Register.getRegFromName(addressingMode.addressRegister.text) to RWM_MODIFY_L) is M68kAddressRegisterIndirectPostIncAddressingMode -> listOf(Register.getRegFromName(addressingMode.addressRegister.text) to (RWM_READ_L or RWM_MODIFY_L))
is M68kAddressRegisterIndirectPreDecAddressingMode -> listOf(Register.getRegFromName(addressingMode.addressRegister.text) to RWM_MODIFY_L) is M68kAddressRegisterIndirectPreDecAddressingMode -> listOf(Register.getRegFromName(addressingMode.addressRegister.text) to (RWM_READ_L or RWM_MODIFY_L))
is M68kWithAddressRegisterIndirect -> { is M68kWithAddressRegisterIndirect -> {
if (addressingMode is M68kWithIndexRegister) { if (addressingMode is M68kWithIndexRegister) {
listOf( listOf(
@@ -11,7 +11,7 @@ import de.platon42.intellij.plugins.m68k.stubs.M68kSymbolDefinitionStubIndex
object M68kLookupUtil { object M68kLookupUtil {
fun findAllGlobalLabels(project: Project): List<M68kGlobalLabel> { fun findAllGlobalLabels(project: Project): List<M68kGlobalLabel> {
val results: MutableList<M68kGlobalLabel> = ArrayList() val results = ArrayList<M68kGlobalLabel>()
StubIndex.getInstance().processAllKeys(M68kGlobalLabelStubIndex.KEY, project) StubIndex.getInstance().processAllKeys(M68kGlobalLabelStubIndex.KEY, project)
{ {
results.addAll(StubIndex.getElements(M68kGlobalLabelStubIndex.KEY, it, project, GlobalSearchScope.allScope(project), M68kGlobalLabel::class.java)) results.addAll(StubIndex.getElements(M68kGlobalLabelStubIndex.KEY, it, project, GlobalSearchScope.allScope(project), M68kGlobalLabel::class.java))
@@ -21,7 +21,7 @@ object M68kLookupUtil {
} }
fun findAllGlobalLabels(file: M68kFile): List<M68kGlobalLabel> { fun findAllGlobalLabels(file: M68kFile): List<M68kGlobalLabel> {
val results: MutableList<M68kGlobalLabel> = ArrayList() val results = ArrayList<M68kGlobalLabel>()
StubIndex.getInstance().processAllKeys( StubIndex.getInstance().processAllKeys(
M68kGlobalLabelStubIndex.KEY, M68kGlobalLabelStubIndex.KEY,
{ {
@@ -44,7 +44,7 @@ object M68kLookupUtil {
fun findAllLocalLabels(globalLabel: M68kGlobalLabel): List<M68kLocalLabel> { fun findAllLocalLabels(globalLabel: M68kGlobalLabel): List<M68kLocalLabel> {
val statement = PsiTreeUtil.getStubOrPsiParentOfType(globalLabel, M68kStatement::class.java)!! val statement = PsiTreeUtil.getStubOrPsiParentOfType(globalLabel, M68kStatement::class.java)!!
val results: MutableList<M68kLocalLabel> = ArrayList() val results = ArrayList<M68kLocalLabel>()
var currentStatement = PsiTreeUtil.getNextSiblingOfType(statement, M68kStatement::class.java) var currentStatement = PsiTreeUtil.getNextSiblingOfType(statement, M68kStatement::class.java)
while (currentStatement != null) { while (currentStatement != null) {
val child = currentStatement.firstChild val child = currentStatement.firstChild
@@ -56,7 +56,7 @@ object M68kLookupUtil {
} }
fun findAllSymbolDefinitions(project: Project): List<M68kSymbolDefinition> { fun findAllSymbolDefinitions(project: Project): List<M68kSymbolDefinition> {
val results: MutableList<M68kSymbolDefinition> = ArrayList() val results = ArrayList<M68kSymbolDefinition>()
StubIndex.getInstance().processAllKeys(M68kSymbolDefinitionStubIndex.KEY, project) StubIndex.getInstance().processAllKeys(M68kSymbolDefinitionStubIndex.KEY, project)
{ {
results.addAll( results.addAll(
@@ -74,7 +74,7 @@ object M68kLookupUtil {
} }
fun findAllSymbolDefinitions(file: M68kFile): List<M68kSymbolDefinition> { fun findAllSymbolDefinitions(file: M68kFile): List<M68kSymbolDefinition> {
val results: MutableList<M68kSymbolDefinition> = ArrayList() val results = ArrayList<M68kSymbolDefinition>()
StubIndex.getInstance().processAllKeys( StubIndex.getInstance().processAllKeys(
M68kSymbolDefinitionStubIndex.KEY, M68kSymbolDefinitionStubIndex.KEY,
{ {
@@ -97,7 +97,7 @@ object M68kLookupUtil {
fun findAllMacroDefinitions(project: Project): List<M68kMacroDefinition> { fun findAllMacroDefinitions(project: Project): List<M68kMacroDefinition> {
val results: MutableList<M68kMacroDefinition> = ArrayList() val results = ArrayList<M68kMacroDefinition>()
StubIndex.getInstance().processAllKeys(M68kMacroDefinitionStubIndex.KEY, project) StubIndex.getInstance().processAllKeys(M68kMacroDefinitionStubIndex.KEY, project)
{ {
results.addAll( results.addAll(
@@ -115,7 +115,7 @@ object M68kLookupUtil {
} }
fun findAllMacroDefinitions(file: M68kFile): List<M68kMacroDefinition> { fun findAllMacroDefinitions(file: M68kFile): List<M68kMacroDefinition> {
val results: MutableList<M68kMacroDefinition> = ArrayList() val results = ArrayList<M68kMacroDefinition>()
StubIndex.getInstance().processAllKeys( StubIndex.getInstance().processAllKeys(
M68kMacroDefinitionStubIndex.KEY, M68kMacroDefinitionStubIndex.KEY,
{ {
@@ -135,6 +135,4 @@ object M68kLookupUtil {
} }
fun findAllMacroDefinitionNames(project: Project): Collection<String> = StubIndex.getInstance().getAllKeys(M68kMacroDefinitionStubIndex.KEY, project) fun findAllMacroDefinitionNames(project: Project): Collection<String> = StubIndex.getInstance().getAllKeys(M68kMacroDefinitionStubIndex.KEY, project)
} }
@@ -117,7 +117,7 @@ object M68kPsiImplUtil {
// RegisterListAddressingMode // RegisterListAddressingMode
@JvmStatic @JvmStatic
fun getRegisters(element: M68kRegisterListAddressingMode): Set<Register> { fun getRegisters(element: M68kRegisterListAddressingMode): Set<Register> {
val registers: MutableSet<Register> = HashSet() val registers = HashSet<Register>()
element.registerList.forEach { registers.add(Register.getRegFromName(it.text)) } element.registerList.forEach { registers.add(Register.getRegFromName(it.text)) }
element.registerRangeList.forEach { element.registerRangeList.forEach {
var startReg = Register.getRegFromName(it.startRegister.text) var startReg = Register.getRegFromName(it.startRegister.text)
@@ -19,7 +19,7 @@ class M68kChooseByNameContributor : ChooseByNameContributorEx {
// } // }
// override fun getItemsByName(name: String, pattern: String, project: Project, includeNonProjectItems: Boolean): Array<NavigationItem> { // override fun getItemsByName(name: String, pattern: String, project: Project, includeNonProjectItems: Boolean): Array<NavigationItem> {
// val result: MutableList<NavigationItem> = ArrayList() // val result = ArrayList<NavigationItem>()
// processElementsWithName(name, result::add, FindSymbolParameters.wrap(pattern, project, includeNonProjectItems)) // processElementsWithName(name, result::add, FindSymbolParameters.wrap(pattern, project, includeNonProjectItems))
// return result.toTypedArray() // return result.toTypedArray()
// } // }
@@ -28,7 +28,7 @@ class M68kGlobalLabelSymbolReference(element: M68kSymbolReference) :
val refName = ref.element.symbolName val refName = ref.element.symbolName
val project = ref.element.project val project = ref.element.project
val targets: MutableList<M68kNamedElement> = SmartList() val targets = SmartList<M68kNamedElement>()
StubIndex.getInstance() StubIndex.getInstance()
.processElements(M68kGlobalLabelStubIndex.KEY, refName, project, GlobalSearchScope.allScope(project), M68kGlobalLabel::class.java) .processElements(M68kGlobalLabelStubIndex.KEY, refName, project, GlobalSearchScope.allScope(project), M68kGlobalLabel::class.java)
{ {
@@ -21,7 +21,7 @@ class M68kLocalLabelReference(element: M68kSymbolReference) : PsiPolyVariantRefe
fun findLocalLabels(element: M68kSymbolReference, predicate: (M68kLocalLabel) -> Boolean): List<M68kLocalLabel> { fun findLocalLabels(element: M68kSymbolReference, predicate: (M68kLocalLabel) -> Boolean): List<M68kLocalLabel> {
val statement = PsiTreeUtil.getStubOrPsiParentOfType(element, M68kStatement::class.java)!! val statement = PsiTreeUtil.getStubOrPsiParentOfType(element, M68kStatement::class.java)!!
val results: MutableList<M68kLocalLabel> = SmartList() val results = SmartList<M68kLocalLabel>()
// go backward // go backward
var currentStatement = PsiTreeUtil.getPrevSiblingOfType(statement, M68kStatement::class.java) var currentStatement = PsiTreeUtil.getPrevSiblingOfType(statement, M68kStatement::class.java)
while (currentStatement != null) { while (currentStatement != null) {
@@ -25,7 +25,7 @@ class M68kMacroReference(element: M68kMacroCall) :
val macroName = ref.element.macroName val macroName = ref.element.macroName
val project = ref.element.project val project = ref.element.project
val targets: MutableList<M68kMacroDefinition> = SmartList() val targets = SmartList<M68kMacroDefinition>()
StubIndex.getInstance() StubIndex.getInstance()
.processElements(M68kMacroDefinitionStubIndex.KEY, macroName, project, GlobalSearchScope.allScope(project), M68kMacroDefinition::class.java) .processElements(M68kMacroDefinitionStubIndex.KEY, macroName, project, GlobalSearchScope.allScope(project), M68kMacroDefinition::class.java)
{ {
@@ -1,6 +1,7 @@
package de.platon42.intellij.plugins.m68k.utils package de.platon42.intellij.plugins.m68k.utils
import de.platon42.intellij.plugins.m68k.asm.* import de.platon42.intellij.plugins.m68k.asm.*
import de.platon42.intellij.plugins.m68k.asm.ConditionCode.Companion.getCcFromMnemonic
import de.platon42.intellij.plugins.m68k.psi.M68kAddressModeUtil import de.platon42.intellij.plugins.m68k.psi.M68kAddressModeUtil
import de.platon42.intellij.plugins.m68k.psi.M68kAsmInstruction import de.platon42.intellij.plugins.m68k.psi.M68kAsmInstruction
import de.platon42.intellij.plugins.m68k.psi.M68kSpecialRegisterDirectAddressingMode import de.platon42.intellij.plugins.m68k.psi.M68kSpecialRegisterDirectAddressingMode
@@ -34,6 +35,30 @@ object M68kIsaUtil {
return matchedIsaData.map { it to M68kIsa.findMatchingAddressMode(it.modes, op1, op2, opSize, specialReg) } return matchedIsaData.map { it to M68kIsa.findMatchingAddressMode(it.modes, op1, op2, opSize, specialReg) }
} }
fun checkIfInstructionUsesRegister(instruction: M68kAsmInstruction, register: Register): Boolean {
if (instruction.addressingModeList.isEmpty()) {
return false
}
return instruction.addressingModeList.any { aml -> M68kAddressModeUtil.getReadWriteModifyRegisters(aml, 0).any { it.first == register } }
}
fun evaluateRegisterUse(asmInstruction: M68kAsmInstruction, adrMode: AllowedAdrMode, register: Register): List<Int> {
val opSize = getOpSizeOrDefault(asmInstruction.asmOp.opSize, adrMode)
val rwm1 = modifyRwmWithOpsize((adrMode.modInfo ushr RWM_OP1_SHIFT) and RWM_OP_MASK, opSize)
val rwm2 = if (asmInstruction.addressingModeList.size > 1) modifyRwmWithOpsize((adrMode.modInfo ushr RWM_OP2_SHIFT) and RWM_OP_MASK, opSize) else 0
return M68kAddressModeUtil.getReadWriteModifyRegisters(asmInstruction.addressingModeList[0], rwm1).asSequence()
.plus(M68kAddressModeUtil.getReadWriteModifyRegisters(asmInstruction.addressingModeList.getOrNull(1), rwm2))
.plus(M68kAddressModeUtil.getOtherReadWriteModifyRegisters(adrMode.modInfo))
.filter { it.first == register }
.map { it.second }
.distinct()
.toList()
}
fun getConcreteTestedCcFromMnemonic(mnemonic: String, isaData: IsaData, adrMode: AllowedAdrMode) =
if (isaData.conditionCodes.isNotEmpty()) getCcFromMnemonic(isaData.mnemonic, mnemonic).testedCc else adrMode.testedCc
fun getOpSizeOrDefault(opSize: Int, adrMode: AllowedAdrMode): Int { fun getOpSizeOrDefault(opSize: Int, adrMode: AllowedAdrMode): Int {
if (opSize == OP_UNSIZED && (adrMode.size != OP_UNSIZED)) { if (opSize == OP_UNSIZED && (adrMode.size != OP_UNSIZED)) {
return if ((adrMode.size and OP_SIZE_W) == OP_SIZE_W) { return if ((adrMode.size and OP_SIZE_W) == OP_SIZE_W) {
+6
View File
@@ -50,6 +50,12 @@
<localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kSyntaxInspection" <localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kSyntaxInspection"
displayName="Assembly instruction validity" groupName="M68k" displayName="Assembly instruction validity" groupName="M68k"
enabledByDefault="true" level="ERROR"/> enabledByDefault="true" level="ERROR"/>
<localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kDeadWriteInspection"
displayName="Dead writes to registers" groupName="M68k"
enabledByDefault="true" level="WARNING"/>
<localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kUnexpectedConditionalInstructionInspection"
displayName="Unaffected condition codes before conditional instruction" groupName="M68k"
enabledByDefault="true" level="WARNING"/>
</extensions> </extensions>
<actions> <actions>
@@ -0,0 +1,15 @@
<html>
<body>
Finds dead writes to registers, i.e. writes that will not have any effect.
Issues a weak warning if the instruction affects only condition codes that are later tested.
Analysis is terminated at the next global label or instruction that reads the register or changes control flow
(or preprocessor statements, like conditional IF statements).
<!-- tooltip end -->
<p>Note: As there is no evaluation of macros right now, the inspection might report some false positives.
As an attempt to reduce these false positives, macros containing the register name will abort the analysis.
</p>
</body>
</html>
@@ -0,0 +1,18 @@
<html>
<body>
Usually, it is expected that an instruction checking the condition codes or using
the condition codes is preceded by an instruction that actually affects the condition
codes. This inspection checks that this is the case.
For example, the 'movea' and 'adda' instructions (which can be (and are!) often written
as 'move' and 'add') do not affect the condition codes and a conditional branch
will not work as expected.
However, this does not need to be a programming error. Advanced coders sometimes
make use of the fact that instructions do not change condition codes and thus
optimize the order of execution.
<!-- tooltip end -->
Analysis is terminated at the next global label, macrocall or preprocessor statement.
</body>
</html>
@@ -19,7 +19,47 @@ internal class M68kInstructionDocumentationProviderTest : AbstractDocumentationP
) )
assertThat(generateDocumentation(myFixture)).isEqualToIgnoringWhitespace( assertThat(generateDocumentation(myFixture)).isEqualToIgnoringWhitespace(
""" """
<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"><div>moveq.l</div></td><td valign="top"><div>#&lt;xxx&gt;</div></td><td valign="top"><div>Dn</div></td></tr></table></div> <div class="definition">
<pre><b>Move Quick</b></pre>
</div>
<div class="content">
<table class="sections">
<tr>
<td class="section" valign="top">Mnemonic / CCs</td>
<td class="section" valign="top">Operand 1</td>
<td class="section" valign="top">Operand 2</td>
</tr>
<tr>
<td valign="top">moveq.l
<hr/>
Condition Codes: <br/>
<table class="sections">
<tr>
<td class="section" valign="top">X</td>
<td class="section" valign="top">N</td>
<td class="section" valign="top">Z</td>
<td class="section" valign="top">V</td>
<td class="section" valign="top">C</td>
</tr>
<tr>
<td valign="top">-</td>
<td valign="top">*</td>
<td valign="top">*</td>
<td valign="top">0</td>
<td valign="top">0</td>
</tr>
</table>
X - Not affected<br/>N - From result (usually if negative)<br/>Z - From result (usually if zero)<br/>V - Always cleared<br/>C - Always cleared
</td>
<td valign="top">
<div>#&lt;xxx&gt;</div>
</td>
<td valign="top">
<div>Dn</div>
</td>
</tr>
</table>
</div>
""" """
) )
} }
@@ -39,15 +79,13 @@ internal class M68kInstructionDocumentationProviderTest : AbstractDocumentationP
<div class="content"> <div class="content">
<table class="sections"> <table class="sections">
<tr> <tr>
<td class="section" valign="top">Mnemonic</td> <td class="section" valign="top">Mnemonic / CCs</td>
<td class="section" valign="top">Op1</td> <td class="section" valign="top">Operand</td>
<td class="section" valign="top">Op2</td>
</tr> </tr>
<tr> <tr>
<td valign="top"> <td valign="top">bra.s<br/>bra.b<br/>bra.w
<div>bra.s</div> <hr/>
<div>bra.b</div> Condition Codes: <br/>Not affected.
<div>bra.w</div>
</td> </td>
<td valign="top"> <td valign="top">
<div>(xxx).w|l</div> <div>(xxx).w|l</div>
@@ -11,7 +11,7 @@ import org.junit.jupiter.api.extension.ExtendWith
internal class M68kRegisterFlowDocumentationProviderTest : AbstractDocumentationProviderTest() { internal class M68kRegisterFlowDocumentationProviderTest : AbstractDocumentationProviderTest() {
@Test @Test
internal fun check_documentation_for_a_register_in_code_flow(@MyFixture myFixture: CodeInsightTestFixture) { internal fun check_documentation_for_a_read_register_in_code_flow(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByText( myFixture.configureByText(
"documentme.asm", """ "documentme.asm", """
label label
@@ -34,7 +34,7 @@ nextlabel
assertThat(generateDocumentation(myFixture)) assertThat(generateDocumentation(myFixture))
.isEqualToIgnoringWhitespace( .isEqualToIgnoringWhitespace(
""" """
<div class="definition">move instruction reads d0.w</div> <div class="definition">move instruction uses d0.w</div>
<table class="sections" style="padding-left: 8pt; padding-right: 8pt"> <table class="sections" style="padding-left: 8pt; padding-right: 8pt">
<tr> <tr>
<td valign="top">        </td> <td valign="top">        </td>
@@ -44,7 +44,7 @@ nextlabel
<tr> <tr>
<td valign="top">        </td> <td valign="top">        </td>
<td valign="top"><code>add.w #1,<font color="#ffc800">d0</font></code></td> <td valign="top"><code>add.w #1,<font color="#ffc800">d0</font></code></td>
<td valign="top"> ; modifies d0.w</td> <td valign="top"> ; changes d0.w</td>
</tr> </tr>
<tr> <tr>
<td valign="top">&nbsp;</td> <td valign="top">&nbsp;</td>
@@ -56,7 +56,7 @@ nextlabel
<tr> <tr>
<td valign="top">        </td> <td valign="top">        </td>
<td valign="top"><code>move.w <font color="#ffc800">d0</font>,d1</code></td> <td valign="top"><code>move.w <font color="#ffc800">d0</font>,d1</code></td>
<td valign="top"> ; reads d0.w</td> <td valign="top"> ; uses d0.w</td>
</tr> </tr>
<tr> <tr>
<td valign="top">        </td> <td valign="top">        </td>
@@ -66,7 +66,7 @@ nextlabel
<tr> <tr>
<td valign="top">        </td> <td valign="top">        </td>
<td valign="top"><code>addq.b #1,<font color="#ffc800">d0</font></code></td> <td valign="top"><code>addq.b #1,<font color="#ffc800">d0</font></code></td>
<td valign="top"> ; modifies d0.b</td> <td valign="top"> ; changes d0.b</td>
</tr> </tr>
<tr> <tr>
<td valign="top">--&gt;</td> <td valign="top">--&gt;</td>
@@ -76,7 +76,7 @@ nextlabel
<tr> <tr>
<td valign="top">        </td> <td valign="top">        </td>
<td valign="top"><code>move.l <font color="#ffc800">d0</font>,d2</code></td> <td valign="top"><code>move.l <font color="#ffc800">d0</font>,d2</code></td>
<td valign="top"> ; reads d0.l</td> <td valign="top"> ; uses d0.l</td>
</tr> </tr>
<tr> <tr>
<td valign="top">&nbsp;</td> <td valign="top">&nbsp;</td>
@@ -96,7 +96,88 @@ nextlabel
<td valign="top"> ; <font color="#00ff00">sets d0.l</font></td> <td valign="top"> ; <font color="#00ff00">sets d0.l</font></td>
</tr> </tr>
</table> </table>
""".trimIndent() """
)
}
@Test
internal fun check_documentation_for_a_written_register_in_code_flow(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByText(
"documentme.asm", """
label
moveq.l #0,d0
add.w #1,d0
move.l d1,-(sp)
move.w d0,d1
move.b d2,d0
addq.b #1,d0
move.w d0,d<caret>1
move.l d0,d2
move.w d1,d2
clr.b d0
moveq.l #0,d0
move.l (sp)+,d1
rts
nextlabel
"""
)
assertThat(generateDocumentation(myFixture))
.isEqualToIgnoringWhitespace(
"""
<div class="definition">move instruction sets d1.w</div>
<table class="sections" style="padding-left: 8pt; padding-right: 8pt">
<tr>
<td colspan="3" valign="top"><b>label</b></td>
</tr>
<tr>
<td valign="top">        </td>
<td valign="top"><code>move.l <font color="#ffc800">d1</font>,-(sp)</code></td>
<td valign="top"> ; uses d1.l</td>
</tr>
<tr>
<td valign="top">        </td>
<td valign="top"><code>move.w d0,<font color="#ffc800">d1</font></code></td>
<td valign="top"> ; sets d1.w</td>
</tr>
<tr>
<td valign="top">&nbsp;</td>
<td valign="top">
<div class="grayed">[...]</div>
</td>
<td valign="top">&nbsp;</td>
</tr>
<tr>
<td valign="top">--&gt;</td>
<td valign="top"><b><code>move.w d0,<font color="#ffc800">d1</font></code></b></td>
<td valign="top"> ; &lt;--</td>
</tr>
<tr>
<td valign="top">&nbsp;</td>
<td valign="top">
<div class="grayed">[...]</div>
</td>
<td valign="top">&nbsp;</td>
</tr>
<tr>
<td valign="top">        </td>
<td valign="top"><code>move.w <font color="#ffc800">d1</font>,d2</code></td>
<td valign="top"> ; uses d1.w</td>
</tr>
<tr>
<td valign="top">&nbsp;</td>
<td valign="top">
<div class="grayed">[...]</div>
</td>
<td valign="top">&nbsp;</td>
</tr>
<tr>
<td valign="top">        </td>
<td valign="top"><code>move.l (sp)+,<font color="#ffc800">d1</font></code></td>
<td valign="top"> ; <font color="#00ff00">sets d1.l</font></td>
</tr>
</table>
"""
) )
} }
} }
@@ -11,6 +11,7 @@ import org.junit.jupiter.api.extension.ExtendWith
@ExtendWith(LightCodeInsightExtension::class) @ExtendWith(LightCodeInsightExtension::class)
@TestDataPath("src/test/resources/inspections") @TestDataPath("src/test/resources/inspections")
abstract class AbstractInspectionTest : AbstractM68kTest() { abstract class AbstractInspectionTest : AbstractM68kTest() {
protected fun assertHighlightings(myFixture: CodeInsightTestFixture, count: Int, snippet: String) { protected fun assertHighlightings(myFixture: CodeInsightTestFixture, count: Int, snippet: String) {
assertThat(myFixture.doHighlighting()) assertThat(myFixture.doHighlighting())
.areExactly(count, Condition({ it.description?.contains(snippet) ?: false }, "containing")) .areExactly(count, Condition({ it.description?.contains(snippet) ?: false }, "containing"))
@@ -0,0 +1,211 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.MyFixture
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
internal class M68kDeadWriteInspectionTest : AbstractInspectionTest() {
@Test
internal fun find_direct_dead_write_to_register(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.l #123,d1
add.l d0,d2
moveq.l #123,d1
"""
)
assertHighlightings(myFixture, 1, "Register d1 is overwritten later without being used")
}
@Test
internal fun find_direct_dead_write_to_register_with_modification(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.l #123,d1
add.l d0,d1
moveq.l #123,d1
"""
)
assertHighlightings(myFixture, 1, "Register d1 is overwritten later without being used")
}
@Test
internal fun no_dead_write_to_register_due_to_part_modification(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.l #123,d1
add.b d1,d0
moveq.l #123,d1
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun use_of_condition_code_causes_weak_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.l #123,d1
scc d0
moveq.l #123,d1
"""
)
assertHighlightings(myFixture, 1, "Register d1 is overwritten later (only CC evaluated?)")
}
@Test
internal fun use_of_conditional_instruction_for_non_conditional_causes_full_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.l #123,d1
st d0
moveq.l #123,d1
"""
)
assertHighlightings(myFixture, 1, "Register d1 is overwritten later without being used")
}
@Test
internal fun use_of_condition_code_after_modification_causes_no_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.l #123,d1
add.b d0,d1
seq .foo
moveq.l #123,d1
.foo
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun use_of_control_flow_causes_no_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.l #123,d1
add.b d0,d1
bra.s .foo
moveq.l #123,d1
.foo
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun movem_can_cause_multiple_warnings(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
movem.l (a0),d0-d7
add.l d0,d1
moveq.l #123,d3
clr.l #123,d6
movem.l (a0),d5-d7
"""
)
assertHighlightings(myFixture, 1, "Register d5 is overwritten later without being used")
assertHighlightings(myFixture, 1, "Register d6 is overwritten later without being used")
assertHighlightings(myFixture, 1, "Register d7 is overwritten later without being used")
}
@Test
internal fun overwrite_in_same_instruction_with_read_causes_no_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.w #123,d3
move.w (a0,d3.w),d3
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun partial_overwrite_in_causes_no_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.w #123,d3
move.b d2,d3
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun oversized_overwrite_in_causes_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.w #123,d3
move.l d2,d3
"""
)
assertHighlightings(myFixture, 1, "Register d3 is overwritten later without being used")
}
@Test
internal fun conditional_write_does_not_cause_a_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
IF 0
move.w #123,d3
ELSE
clr.w d3
ENDC
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun modification_extending_width_does_not_cause_a_warning_on_smaller_write_later(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.b (a0)+,d0
lsl #8,d0
move.b (a0)+,d0
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun macro_call_with_register_name_will_not_cause_a_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
moveq.l #-1,d0
COPRMOVE d0,bltafwm
move.l pd_CurrPlanesPtr(a4),d0
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun macro_call_with_different_register_name_will_cause_a_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
moveq.l #-1,d0
COPRMOVE d1,bltafwm
move.l pd_CurrPlanesPtr(a4),d0
"""
)
assertHighlightings(myFixture, 1, "Register d0 is overwritten later without being used")
}
}
@@ -0,0 +1,74 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.MyFixture
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
internal class M68kUnexpectedConditionalInstructionInspectionTest : AbstractInspectionTest() {
@Test
internal fun movea_causes_warning_when_used_for_conditional_branching(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kUnexpectedConditionalInstructionInspection::class.java)
myFixture.configureByText(
"unexpectedcc.asm", """
move.l d0,a1
bne.s .cont
rts
.cont
"""
)
assertHighlightings(myFixture, 1, "Condition codes unaffected by instruction (movea - Move Address)")
}
@Test
internal fun no_warning_on_consecutive_conditional_branches(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kUnexpectedConditionalInstructionInspection::class.java)
myFixture.configureByText(
"unexpectedcc.asm", """
move.b P61_arplist(pc,d0),d0
beq.b .arp0
bmi.b .arp1
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun no_warning_on_macro_call_inbetween(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kUnexpectedConditionalInstructionInspection::class.java)
myFixture.configureByText(
"unexpectedcc.asm", """
move.l pd_PalCurShamPalPtr(a4),a1
PALSTEPDOWN
bne.s .loopline
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun no_warning_flow_control_instruction(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kUnexpectedConditionalInstructionInspection::class.java)
myFixture.configureByText(
"unexpectedcc.asm", """
bsr foo
bne.s .loopline
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun warning_on_conditional_set_series_with_suba(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kUnexpectedConditionalInstructionInspection::class.java)
myFixture.configureByText(
"unexpectedcc.asm", """
sub.l a0,a0
seq d0
sne d1
"""
)
assertHighlightings(myFixture, 1, "Condition codes unaffected by instruction (suba - Subtract Address)")
}
}