Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c3daf28f7 | ||
|
|
1dcf288d27 | ||
|
|
2abb5af8b0 | ||
|
|
6c9a726b86 | ||
|
|
921449cbb8 | ||
|
|
6f99c2ffcc | ||
|
|
5881dcdaf8 | ||
|
|
71398f51d2 | ||
|
|
eb26793a20 |
@@ -2,7 +2,7 @@
|
||||
|
||||
_MC68000 Assembly Language Plugin_ is plugin for Jetbrains IDEs (CLion, IntelliJ, etc.).
|
||||
|
||||

|
||||

|
||||
|
||||
## 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.
|
||||
|
||||
My plugin, on the other hand, is still pretty basic and is the result of about two weeks of work. I released a really early first version it because I think
|
||||
it's "good enough" to get started, and I can return to demo coding with its current state.
|
||||
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 "
|
||||
good enough" to get started, and I can return to demo coding with its current state.
|
||||
|
||||
## Features
|
||||
|
||||
- Parser / Lexer for MC68000 (yes, only 68000 right now!) assembly language files in VAsm / DevPac style
|
||||
- Inspection for validating the syntax the 68000 ISA.
|
||||
- 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!)
|
||||
- Mnemonics 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
|
||||
- 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
|
||||
|
||||
- `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).
|
||||
- No support for other processor instructions, FPU or 68020+ address modes.
|
||||
- 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:
|
||||
- Macro evaluation on invocation
|
||||
- Folding
|
||||
- Semantic inspections
|
||||
- More semantic inspections
|
||||
- Quick fixes
|
||||
- Formatter + Code Style Settings
|
||||
- Maybe formatter + Code Style Settings
|
||||
- Register use analysis (but this only makes sense after macro evaluation)
|
||||
- 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
|
||||
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
|
||||
|
||||
### 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)
|
||||
|
||||
- Notice: Due to major new API use, this plugin no longer works on IDEs >=2019.3.1, but rather requires >=2020.3.
|
||||
|
||||
+13
-21
@@ -7,7 +7,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = 'de.platon42'
|
||||
version = '0.4'
|
||||
version = '0.5'
|
||||
sourceCompatibility = "1.8"
|
||||
targetCompatibility = "1.8"
|
||||
|
||||
@@ -57,6 +57,18 @@ runPluginVerifier {
|
||||
|
||||
patchPluginXml {
|
||||
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>
|
||||
<ul>
|
||||
<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>New: When asking for documentation on registers, a code flow analysis is done. Cool stuff!
|
||||
</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>
|
||||
""")
|
||||
}
|
||||
|
||||
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_W = 0xb00
|
||||
const val RWM_READ_L = 0xf00
|
||||
const val RWM_READ_SHIFT = 8
|
||||
|
||||
const val RWM_MODIFY_OPSIZE = 0x880
|
||||
const val RWM_MODIFY_B = 0x990
|
||||
const val RWM_MODIFY_W = 0xbb0
|
||||
const val RWM_MODIFY_L = 0xff0
|
||||
const val RWM_MODIFY_OPSIZE = 0x080
|
||||
const val RWM_MODIFY_B = 0x090
|
||||
const val RWM_MODIFY_W = 0x0b0
|
||||
const val RWM_MODIFY_L = 0x0f0
|
||||
const val RWM_MODIFY_SHIFT = 4
|
||||
|
||||
const val RWM_OP1_SHIFT = 0
|
||||
const val RWM_OP2_SHIFT = 12
|
||||
@@ -118,7 +120,9 @@ data class AllowedAdrMode(
|
||||
val op2: Set<AddressMode>? = null,
|
||||
val size: Int = OP_SIZE_BWL,
|
||||
val specialReg: String? = null,
|
||||
val modInfo: Int = 0
|
||||
val modInfo: Int = 0,
|
||||
val affectedCc: Int = 0,
|
||||
val testedCc: Int = 0
|
||||
)
|
||||
|
||||
data class IsaData(
|
||||
@@ -131,6 +135,7 @@ data class IsaData(
|
||||
val isPrivileged: Boolean = false,
|
||||
val hasOps: Boolean = true,
|
||||
val modes: List<AllowedAdrMode> = listOf(AllowedAdrMode()),
|
||||
val changesControlFlow: Boolean = false
|
||||
)
|
||||
|
||||
object M68kIsa {
|
||||
@@ -210,38 +215,78 @@ object M68kIsa {
|
||||
private val DREG_ONLY = setOf(AddressMode.DATA_REGISTER_DIRECT)
|
||||
|
||||
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(
|
||||
setOf(AddressMode.ADDRESS_REGISTER_DIRECT),
|
||||
setOf(AddressMode.DATA_REGISTER_DIRECT),
|
||||
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(
|
||||
AllowedAdrMode(DREG_ONLY, DREG_ONLY, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE),
|
||||
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), DREG_ONLY, modInfo = RWM_MODIFY_OP2_OPSIZE),
|
||||
AllowedAdrMode(INDIRECT_MODES, null, modInfo = RWM_MODIFY_OP1_OPSIZE),
|
||||
private val ADDQ_SUBQ_MODES = listOf(
|
||||
AllowedAdrMode(
|
||||
setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, modInfo = RWM_MODIFY_OP2_OPSIZE, affectedCc = cc("C****")
|
||||
),
|
||||
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(
|
||||
AllowedAdrMode(DREG_ONLY, DREG_ONLY, OP_SIZE_L, modInfo = RWM_READ_OP1_B or RWM_MODIFY_OP2_L),
|
||||
AllowedAdrMode(DREG_ONLY, INDIRECT_MODES, OP_SIZE_B, modInfo = RWM_READ_OP1_B or RWM_MODIFY_OP2_B),
|
||||
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), DREG_ONLY, OP_SIZE_L, modInfo = RWM_MODIFY_OP2_L),
|
||||
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), INDIRECT_MODES, OP_SIZE_B, modInfo = RWM_MODIFY_OP2_B),
|
||||
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, affectedCc = cc("--*--")),
|
||||
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, affectedCc = cc("--*--")),
|
||||
)
|
||||
|
||||
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, INDIRECT_MODES, OP_SIZE_B, modInfo = RWM_READ_OP1_B or RWM_READ_OP2_B),
|
||||
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), DREG_ONLY, OP_SIZE_L, modInfo = RWM_READ_OP2_L),
|
||||
AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), INDIRECT_MODES, OP_SIZE_B, modInfo = RWM_READ_OP2_B),
|
||||
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, affectedCc = cc("--*--")),
|
||||
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, affectedCc = cc("--*--")),
|
||||
)
|
||||
|
||||
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" }
|
||||
|
||||
@@ -250,7 +295,12 @@ object M68kIsa {
|
||||
// Data Movement Instructions
|
||||
IsaData(
|
||||
"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(
|
||||
"movea", "Move Address", altMnemonics = listOf("move"),
|
||||
@@ -283,7 +333,7 @@ object M68kIsa {
|
||||
),
|
||||
setOf(AddressMode.REGISTER_LIST, AddressMode.ADDRESS_REGISTER_DIRECT, AddressMode.DATA_REGISTER_DIRECT),
|
||||
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
|
||||
AllowedAdrMode(
|
||||
@@ -332,7 +382,7 @@ object M68kIsa {
|
||||
|
||||
IsaData(
|
||||
"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(
|
||||
@@ -342,7 +392,7 @@ object M68kIsa {
|
||||
setOf(AddressMode.DATA_REGISTER_DIRECT, AddressMode.ADDRESS_REGISTER_DIRECT),
|
||||
setOf(AddressMode.DATA_REGISTER_DIRECT, AddressMode.ADDRESS_REGISTER_DIRECT),
|
||||
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(
|
||||
"adda", "Add Address", altMnemonics = listOf("add"),
|
||||
modes = listOf(
|
||||
AllowedAdrMode(ALL_68000_MODES, AREG_ONLY, OP_SIZE_WL, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_L),
|
||||
)
|
||||
modes = listOf(AllowedAdrMode(ALL_68000_MODES, AREG_ONLY, OP_SIZE_WL, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_L))
|
||||
),
|
||||
IsaData(
|
||||
"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(
|
||||
AllowedAdrMode(DREG_ONLY, DREG_ONLY, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE),
|
||||
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
|
||||
setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, modInfo = RWM_MODIFY_OP2_OPSIZE, affectedCc = cc("C****")
|
||||
)
|
||||
)
|
||||
),
|
||||
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(
|
||||
@@ -428,49 +465,67 @@ object M68kIsa {
|
||||
),
|
||||
IsaData(
|
||||
"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(
|
||||
"subq", "Subtract 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)
|
||||
"neg", "Negate", modes = listOf(
|
||||
AllowedAdrMode(
|
||||
ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, modInfo = RWM_MODIFY_OP1_OPSIZE,
|
||||
affectedCc = cc("C****")
|
||||
)
|
||||
)
|
||||
),
|
||||
IsaData(
|
||||
"subx", "Subtract with Extend",
|
||||
modes = listOf(
|
||||
AllowedAdrMode(DREG_ONLY, DREG_ONLY, modInfo = RWM_READ_OP1_OPSIZE or RWM_MODIFY_OP2_OPSIZE),
|
||||
"negx", "Negate with Extend", modes = listOf(
|
||||
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
|
||||
ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, modInfo = RWM_MODIFY_OP1_OPSIZE,
|
||||
affectedCc = cc("C****"), testedCc = cc("?----")
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
IsaData("neg", "Negate", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, modInfo = RWM_MODIFY_OP1_OPSIZE))),
|
||||
IsaData("negx", "Negate with Extend", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, modInfo = RWM_MODIFY_OP1_OPSIZE))),
|
||||
|
||||
IsaData("clr", "Clear", modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, modInfo = RWM_SET_OP1_OPSIZE))),
|
||||
IsaData(
|
||||
"clr",
|
||||
"Clear",
|
||||
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, null, modInfo = RWM_SET_OP1_OPSIZE, affectedCc = cc("-0100")))
|
||||
),
|
||||
|
||||
IsaData(
|
||||
"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(
|
||||
setOf(AddressMode.ADDRESS_REGISTER_DIRECT),
|
||||
setOf(AddressMode.DATA_REGISTER_DIRECT),
|
||||
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(
|
||||
"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(
|
||||
"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(
|
||||
"cmpm", "Compare Memory to Memory", altMnemonics = listOf("cmp"),
|
||||
@@ -478,76 +533,110 @@ object M68kIsa {
|
||||
AllowedAdrMode(
|
||||
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(
|
||||
"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(
|
||||
"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(
|
||||
"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(
|
||||
"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
|
||||
IsaData(
|
||||
"and", "Logical AND",
|
||||
modes = listOf(
|
||||
AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, 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)
|
||||
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, affectedCc = cc("-**00"))
|
||||
)
|
||||
),
|
||||
IsaData(
|
||||
"andi", "Logical AND Immediate", altMnemonics = listOf("and"),
|
||||
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL, modInfo = RWM_MODIFY_OP2_OPSIZE))
|
||||
"andi", "Logical AND Immediate",
|
||||
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(
|
||||
"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(
|
||||
"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(
|
||||
"or", "Logical Inclusive-OR",
|
||||
modes = listOf(
|
||||
AllowedAdrMode(ALL_EXCEPT_AREG, DREG_ONLY, 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)
|
||||
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, affectedCc = cc("-**00"))
|
||||
)
|
||||
),
|
||||
IsaData(
|
||||
"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(
|
||||
"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
|
||||
IsaData("asl", "Arithmetic Shift Left", modes = ASD_LSD_ROD_ROXD_MODES),
|
||||
IsaData("asr", "Arithmetic Shift Right", modes = ASD_LSD_ROD_ROXD_MODES),
|
||||
IsaData("lsl", "Logical Shift Left", modes = ASD_LSD_ROD_ROXD_MODES),
|
||||
IsaData("lsr", "Logical Shift Right", modes = ASD_LSD_ROD_ROXD_MODES),
|
||||
IsaData("rol", "Rotate Left", modes = ASD_LSD_ROD_ROXD_MODES),
|
||||
IsaData("ror", "Rotate Right", modes = ASD_LSD_ROD_ROXD_MODES),
|
||||
IsaData("roxl", "Rotate with Extend Left", modes = ASD_LSD_ROD_ROXD_MODES),
|
||||
IsaData("roxr", "Rotate with Extend Right", modes = ASD_LSD_ROD_ROXD_MODES),
|
||||
IsaData("swap", "Swap Register Words", modes = listOf(AllowedAdrMode(DREG_ONLY, null, OP_SIZE_W, modInfo = RWM_MODIFY_OP1_L))),
|
||||
IsaData("asl", "Arithmetic Shift Left", modes = ASD_LSD_MODES),
|
||||
IsaData("asr", "Arithmetic Shift Right", modes = ASD_LSD_MODES),
|
||||
IsaData("lsl", "Logical Shift Left", modes = ASD_LSD_MODES),
|
||||
IsaData("lsr", "Logical Shift Right", modes = ASD_LSD_MODES),
|
||||
IsaData("rol", "Rotate Left", modes = ROD_MODES),
|
||||
IsaData("ror", "Rotate Right", modes = ROD_MODES),
|
||||
IsaData("roxl", "Rotate with Extend Left", modes = 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, affectedCc = cc("-**00")))
|
||||
),
|
||||
|
||||
// Bit Manipulation Instructions
|
||||
IsaData("bchg", "Test Bit and Change", modes = BCHG_BCLR_BSET_MODES),
|
||||
@@ -559,42 +648,61 @@ object M68kIsa {
|
||||
IsaData(
|
||||
"abcd", "Add Decimal with Extend",
|
||||
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(
|
||||
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC),
|
||||
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC),
|
||||
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(
|
||||
"sbcd", "Subtract Decimal with Extend",
|
||||
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(
|
||||
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC),
|
||||
setOf(AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC),
|
||||
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(
|
||||
"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
|
||||
IsaData(
|
||||
"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(
|
||||
"bsr",
|
||||
"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(
|
||||
@@ -602,11 +710,12 @@ object M68kIsa {
|
||||
"Test Condition, Decrement, and Branch",
|
||||
altMnemonics = listOf("dbra"),
|
||||
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(
|
||||
"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(
|
||||
@@ -622,7 +731,8 @@ object M68kIsa {
|
||||
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX
|
||||
), null, OP_UNSIZED
|
||||
)
|
||||
)
|
||||
),
|
||||
changesControlFlow = true
|
||||
),
|
||||
IsaData(
|
||||
"jsr", "Jump to Subroutine",
|
||||
@@ -637,27 +747,60 @@ object M68kIsa {
|
||||
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX
|
||||
), null, OP_UNSIZED, modInfo = RWM_MODIFY_STACK
|
||||
)
|
||||
)
|
||||
),
|
||||
changesControlFlow = true
|
||||
),
|
||||
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("rts", "Return from Subroutine", hasOps = false, modes = listOf(AllowedAdrMode(size = OP_UNSIZED, modInfo = RWM_MODIFY_STACK))),
|
||||
IsaData(
|
||||
"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
|
||||
IsaData(
|
||||
"andi", "AND Immediate to Status Register", id = "andi to SR", altMnemonics = listOf("and"), isPrivileged = true,
|
||||
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr"))
|
||||
modes = listOf(
|
||||
AllowedAdrMode(
|
||||
setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr",
|
||||
affectedCc = cc("AAAAA"), testedCc = cc("?????")
|
||||
)
|
||||
)
|
||||
),
|
||||
IsaData(
|
||||
"eori", "Exclusive-OR Immediate to Status Register", id = "eori to SR", altMnemonics = listOf("eor"), isPrivileged = true,
|
||||
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr"))
|
||||
modes = listOf(
|
||||
AllowedAdrMode(
|
||||
setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr",
|
||||
affectedCc = cc("*****"), testedCc = cc("?????")
|
||||
)
|
||||
)
|
||||
),
|
||||
IsaData(
|
||||
"ori", "Inclusive-OR Immediate to Status Register", id = "ori to SR", altMnemonics = listOf("or"), isPrivileged = true,
|
||||
modes = listOf(AllowedAdrMode(setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr"))
|
||||
modes = listOf(
|
||||
AllowedAdrMode(
|
||||
setOf(AddressMode.IMMEDIATE_DATA), setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr",
|
||||
affectedCc = cc("OOOOO"), testedCc = cc("?????")
|
||||
)
|
||||
)
|
||||
),
|
||||
IsaData(
|
||||
"move", "Move from Status Register", id = "move from SR",
|
||||
@@ -667,13 +810,19 @@ object M68kIsa {
|
||||
ALL_EXCEPT_AREG_IMMEDIATE_AND_PC_REL,
|
||||
OP_SIZE_W,
|
||||
"sr",
|
||||
modInfo = RWM_SET_OP2_W
|
||||
modInfo = RWM_SET_OP2_W,
|
||||
testedCc = cc("?????")
|
||||
)
|
||||
)
|
||||
),
|
||||
IsaData(
|
||||
"move", "Move to Status Register", id = "move to SR", isPrivileged = true,
|
||||
modes = listOf(AllowedAdrMode(ALL_EXCEPT_AREG, setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr"))
|
||||
modes = listOf(
|
||||
AllowedAdrMode(
|
||||
ALL_EXCEPT_AREG, setOf(AddressMode.SPECIAL_REGISTER_DIRECT), OP_SIZE_W, "sr",
|
||||
affectedCc = cc("*****")
|
||||
)
|
||||
)
|
||||
),
|
||||
IsaData(
|
||||
"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(
|
||||
"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(
|
||||
"stop", "Stop", isPrivileged = true,
|
||||
@@ -695,59 +845,70 @@ object M68kIsa {
|
||||
|
||||
IsaData(
|
||||
"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(
|
||||
"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(
|
||||
"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(
|
||||
"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(
|
||||
"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
|
||||
IsaData(
|
||||
"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 =
|
||||
isaData.asSequence()
|
||||
.flatMap {
|
||||
if (it.conditionCodes.isEmpty()) it.altMnemonics.plus(it.mnemonic) else it.altMnemonics.plus(it.conditionCodes
|
||||
.map { cc ->
|
||||
it.mnemonic.replace("CC", cc)
|
||||
})
|
||||
}
|
||||
.toSet()
|
||||
private val mnemonicLookupMap = isaData.asSequence()
|
||||
.flatMap {
|
||||
(if (it.conditionCodes.isEmpty()) it.altMnemonics.plus(it.mnemonic) else it.altMnemonics.plus(it.conditionCodes
|
||||
.map { cc ->
|
||||
it.mnemonic.replace("CC", cc)
|
||||
})).map { mnemonic -> mnemonic to it }
|
||||
}
|
||||
.groupBy({ it.first }) { it.second }
|
||||
|
||||
val mnemonics = mnemonicLookupMap.keys
|
||||
|
||||
fun findMatchingInstructions(mnemonic: String): List<IsaData> {
|
||||
val lowerMnemonic = mnemonic.lowercase()
|
||||
return isaData
|
||||
.filter {
|
||||
if (it.conditionCodes.isEmpty()) {
|
||||
(it.mnemonic == lowerMnemonic) || it.altMnemonics.any { altMnemonic -> altMnemonic == lowerMnemonic }
|
||||
} else {
|
||||
it.altMnemonics.any { altMnemonic -> altMnemonic == lowerMnemonic } ||
|
||||
it.conditionCodes.any { cc ->
|
||||
it.mnemonic.replace("CC", cc) == lowerMnemonic
|
||||
}
|
||||
}
|
||||
}
|
||||
return mnemonicLookupMap.getOrDefault(mnemonic.lowercase(), emptyList())
|
||||
}
|
||||
|
||||
fun findMatchingOpMode(candidates: List<IsaData>, op1: AddressMode?, op2: AddressMode?, opSize: Int?, specialReg: String?): List<IsaData> {
|
||||
|
||||
+61
-17
@@ -10,6 +10,7 @@ import com.intellij.psi.PsiFile
|
||||
import de.platon42.intellij.plugins.m68k.asm.AddressMode
|
||||
import de.platon42.intellij.plugins.m68k.asm.IsaData
|
||||
import de.platon42.intellij.plugins.m68k.asm.M68kIsa
|
||||
import de.platon42.intellij.plugins.m68k.asm.getCcInfo
|
||||
import de.platon42.intellij.plugins.m68k.psi.M68kAsmInstruction
|
||||
import de.platon42.intellij.plugins.m68k.psi.M68kAsmOp
|
||||
import de.platon42.intellij.plugins.m68k.psi.M68kOperandSize
|
||||
@@ -36,24 +37,67 @@ class M68kInstructionDocumentationProvider : AbstractDocumentationProvider() {
|
||||
val defBuilder = createDefinition(isaData)
|
||||
builder.append(defBuilder.wrapWith(DocumentationMarkup.DEFINITION_ELEMENT))
|
||||
|
||||
val hasSameCcsForEverything = isaData.modes.map { it.affectedCc }.distinct().count() == 1
|
||||
var alreadyShownCcsOnce = false
|
||||
val mnemonicInfoRows = HtmlBuilder()
|
||||
val headerCells = listOf(
|
||||
HtmlChunk.text("Mnemonic").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL),
|
||||
HtmlChunk.text("Op1").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL),
|
||||
HtmlChunk.text("Op2").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL)
|
||||
)
|
||||
mnemonicInfoRows.append(HtmlChunk.tag("tr").children(headerCells))
|
||||
isaData.modes.forEach { allowedAdrMode ->
|
||||
val mnemonics = findOpSizeDescriptions(allowedAdrMode.size)
|
||||
.map { HtmlChunk.text(isaData.mnemonic + it).wrapWith(HtmlChunk.div()) }
|
||||
mnemonicInfoRows.append(
|
||||
HtmlChunk.tag("tr").children(
|
||||
DocumentationMarkup.SECTION_CONTENT_CELL.children(mnemonics),
|
||||
DocumentationMarkup.SECTION_CONTENT_CELL.child(collectAddressModes(allowedAdrMode.op1)),
|
||||
DocumentationMarkup.SECTION_CONTENT_CELL.child(collectAddressModes(allowedAdrMode.op2))
|
||||
)
|
||||
)
|
||||
}
|
||||
mnemonicInfoRows.appendWithSeparators(HtmlChunk.tag("tr").child(HtmlChunk.hr().wrapWith(DocumentationMarkup.SECTION_CONTENT_CELL).attr("colspan", "3")),
|
||||
isaData.modes.map { allowedAdrMode ->
|
||||
val addressModeInfoRows = HtmlBuilder()
|
||||
val headerCells = if (allowedAdrMode.op2 != null) {
|
||||
listOf(
|
||||
HtmlChunk.text("Mnemonic / CCs").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL),
|
||||
HtmlChunk.text("Operand 1").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL),
|
||||
HtmlChunk.text("Operand 2").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL)
|
||||
)
|
||||
} else if (allowedAdrMode.op1 != null) {
|
||||
listOf(
|
||||
HtmlChunk.text("Mnemonic / CCs").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL),
|
||||
HtmlChunk.text("Operand").wrapWith(DocumentationMarkup.SECTION_HEADER_CELL)
|
||||
)
|
||||
} 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()
|
||||
contentBuilder.append(mnemonicInfoRows.wrapWith(DocumentationMarkup.SECTIONS_TABLE))
|
||||
|
||||
+14
-38
@@ -13,8 +13,8 @@ import com.intellij.ui.JBColor
|
||||
import de.platon42.intellij.plugins.m68k.asm.*
|
||||
import de.platon42.intellij.plugins.m68k.asm.Register.Companion.getRegFromName
|
||||
import de.platon42.intellij.plugins.m68k.psi.*
|
||||
import de.platon42.intellij.plugins.m68k.psi.M68kAddressModeUtil.getOtherReadWriteModifyRegisters
|
||||
import de.platon42.intellij.plugins.m68k.psi.M68kAddressModeUtil.getReadWriteModifyRegisters
|
||||
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.getOpSizeOrDefault
|
||||
import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.modifyRwmWithOpsize
|
||||
@@ -52,7 +52,7 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
|
||||
|
||||
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 backtrace: MutableList<HtmlChunk> = ArrayList()
|
||||
val backtrace = ArrayList<HtmlChunk>()
|
||||
val missingBits = if (cursorRwm and RWM_SET_L != 0) {
|
||||
if (totalRwm and RWM_SET_L == RWM_SET_L) {
|
||||
backtrace.add(
|
||||
@@ -64,10 +64,10 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
|
||||
)
|
||||
0
|
||||
} else {
|
||||
(RWM_SET_L and RWM_SIZE_MASK) and totalRwm.inv()
|
||||
RWM_SIZE_MASK and totalRwm.inv()
|
||||
}
|
||||
} 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 localLabelName = PsiTreeUtil.findChildOfType(initialStatement, M68kLocalLabel::class.java)?.name ?: "-->"
|
||||
@@ -85,7 +85,7 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
|
||||
)
|
||||
})
|
||||
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) {
|
||||
PsiTreeUtil.getNextSiblingOfType(
|
||||
it,
|
||||
@@ -108,10 +108,10 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
|
||||
startingStatement: M68kStatement,
|
||||
linesLimit: Int,
|
||||
direction: (statement: M68kStatement) -> M68kStatement?
|
||||
): MutableList<HtmlChunk> {
|
||||
): List<HtmlChunk> {
|
||||
var missingBits = rwmBits
|
||||
var currStatement = startingStatement
|
||||
val statementLines: MutableList<HtmlChunk> = ArrayList()
|
||||
val statementLines = ArrayList<HtmlChunk>()
|
||||
val rn = register.regname
|
||||
var addAbrevDots = false
|
||||
var lines = 0
|
||||
@@ -189,12 +189,12 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
|
||||
|
||||
private fun rwmToDisplayText(rwm: Int, rn: String) =
|
||||
when (rwm) {
|
||||
RWM_READ_B -> "reads $rn.b"
|
||||
RWM_READ_W -> "reads $rn.w"
|
||||
RWM_READ_L -> "reads $rn.l"
|
||||
RWM_MODIFY_B -> "modifies $rn.b"
|
||||
RWM_MODIFY_W -> "modifies $rn.w"
|
||||
RWM_MODIFY_L -> "modifies $rn.l"
|
||||
RWM_READ_B -> "uses $rn.b"
|
||||
RWM_READ_W -> "uses $rn.w"
|
||||
RWM_READ_L -> "uses $rn.l"
|
||||
RWM_MODIFY_B -> "changes $rn.b"
|
||||
RWM_MODIFY_W -> "changes $rn.w"
|
||||
RWM_MODIFY_L -> "changes $rn.l"
|
||||
RWM_SET_B -> "sets $rn.b"
|
||||
RWM_SET_W -> "sets $rn.w"
|
||||
RWM_SET_L -> "sets $rn.l"
|
||||
@@ -210,30 +210,6 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
|
||||
)
|
||||
.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? {
|
||||
if (contextElement == null) return null
|
||||
if (contextElement is M68kDataRegister || contextElement is M68kAddressRegister) return contextElement
|
||||
|
||||
+104
@@ -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)
|
||||
return arrayOf(
|
||||
when (supportedOpSizes) {
|
||||
|
||||
+54
@@ -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 M68kAbsoluteAddressAddressingMode -> emptyList()
|
||||
|
||||
is M68kAddressRegisterIndirectPostIncAddressingMode -> listOf(Register.getRegFromName(addressingMode.addressRegister.text) to RWM_MODIFY_L)
|
||||
is M68kAddressRegisterIndirectPreDecAddressingMode -> 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_READ_L or RWM_MODIFY_L))
|
||||
is M68kWithAddressRegisterIndirect -> {
|
||||
if (addressingMode is M68kWithIndexRegister) {
|
||||
listOf(
|
||||
|
||||
@@ -11,7 +11,7 @@ import de.platon42.intellij.plugins.m68k.stubs.M68kSymbolDefinitionStubIndex
|
||||
object M68kLookupUtil {
|
||||
|
||||
fun findAllGlobalLabels(project: Project): List<M68kGlobalLabel> {
|
||||
val results: MutableList<M68kGlobalLabel> = ArrayList()
|
||||
val results = ArrayList<M68kGlobalLabel>()
|
||||
StubIndex.getInstance().processAllKeys(M68kGlobalLabelStubIndex.KEY, project)
|
||||
{
|
||||
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> {
|
||||
val results: MutableList<M68kGlobalLabel> = ArrayList()
|
||||
val results = ArrayList<M68kGlobalLabel>()
|
||||
StubIndex.getInstance().processAllKeys(
|
||||
M68kGlobalLabelStubIndex.KEY,
|
||||
{
|
||||
@@ -44,7 +44,7 @@ object M68kLookupUtil {
|
||||
|
||||
fun findAllLocalLabels(globalLabel: M68kGlobalLabel): List<M68kLocalLabel> {
|
||||
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)
|
||||
while (currentStatement != null) {
|
||||
val child = currentStatement.firstChild
|
||||
@@ -56,7 +56,7 @@ object M68kLookupUtil {
|
||||
}
|
||||
|
||||
fun findAllSymbolDefinitions(project: Project): List<M68kSymbolDefinition> {
|
||||
val results: MutableList<M68kSymbolDefinition> = ArrayList()
|
||||
val results = ArrayList<M68kSymbolDefinition>()
|
||||
StubIndex.getInstance().processAllKeys(M68kSymbolDefinitionStubIndex.KEY, project)
|
||||
{
|
||||
results.addAll(
|
||||
@@ -74,7 +74,7 @@ object M68kLookupUtil {
|
||||
}
|
||||
|
||||
fun findAllSymbolDefinitions(file: M68kFile): List<M68kSymbolDefinition> {
|
||||
val results: MutableList<M68kSymbolDefinition> = ArrayList()
|
||||
val results = ArrayList<M68kSymbolDefinition>()
|
||||
StubIndex.getInstance().processAllKeys(
|
||||
M68kSymbolDefinitionStubIndex.KEY,
|
||||
{
|
||||
@@ -97,7 +97,7 @@ object M68kLookupUtil {
|
||||
|
||||
|
||||
fun findAllMacroDefinitions(project: Project): List<M68kMacroDefinition> {
|
||||
val results: MutableList<M68kMacroDefinition> = ArrayList()
|
||||
val results = ArrayList<M68kMacroDefinition>()
|
||||
StubIndex.getInstance().processAllKeys(M68kMacroDefinitionStubIndex.KEY, project)
|
||||
{
|
||||
results.addAll(
|
||||
@@ -115,7 +115,7 @@ object M68kLookupUtil {
|
||||
}
|
||||
|
||||
fun findAllMacroDefinitions(file: M68kFile): List<M68kMacroDefinition> {
|
||||
val results: MutableList<M68kMacroDefinition> = ArrayList()
|
||||
val results = ArrayList<M68kMacroDefinition>()
|
||||
StubIndex.getInstance().processAllKeys(
|
||||
M68kMacroDefinitionStubIndex.KEY,
|
||||
{
|
||||
@@ -135,6 +135,4 @@ object M68kLookupUtil {
|
||||
}
|
||||
|
||||
fun findAllMacroDefinitionNames(project: Project): Collection<String> = StubIndex.getInstance().getAllKeys(M68kMacroDefinitionStubIndex.KEY, project)
|
||||
|
||||
|
||||
}
|
||||
@@ -117,7 +117,7 @@ object M68kPsiImplUtil {
|
||||
// RegisterListAddressingMode
|
||||
@JvmStatic
|
||||
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.registerRangeList.forEach {
|
||||
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> {
|
||||
// val result: MutableList<NavigationItem> = ArrayList()
|
||||
// val result = ArrayList<NavigationItem>()
|
||||
// processElementsWithName(name, result::add, FindSymbolParameters.wrap(pattern, project, includeNonProjectItems))
|
||||
// return result.toTypedArray()
|
||||
// }
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ class M68kGlobalLabelSymbolReference(element: M68kSymbolReference) :
|
||||
val refName = ref.element.symbolName
|
||||
val project = ref.element.project
|
||||
|
||||
val targets: MutableList<M68kNamedElement> = SmartList()
|
||||
val targets = SmartList<M68kNamedElement>()
|
||||
StubIndex.getInstance()
|
||||
.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> {
|
||||
val statement = PsiTreeUtil.getStubOrPsiParentOfType(element, M68kStatement::class.java)!!
|
||||
val results: MutableList<M68kLocalLabel> = SmartList()
|
||||
val results = SmartList<M68kLocalLabel>()
|
||||
// go backward
|
||||
var currentStatement = PsiTreeUtil.getPrevSiblingOfType(statement, M68kStatement::class.java)
|
||||
while (currentStatement != null) {
|
||||
|
||||
@@ -25,7 +25,7 @@ class M68kMacroReference(element: M68kMacroCall) :
|
||||
val macroName = ref.element.macroName
|
||||
val project = ref.element.project
|
||||
|
||||
val targets: MutableList<M68kMacroDefinition> = SmartList()
|
||||
val targets = SmartList<M68kMacroDefinition>()
|
||||
StubIndex.getInstance()
|
||||
.processElements(M68kMacroDefinitionStubIndex.KEY, macroName, project, GlobalSearchScope.allScope(project), M68kMacroDefinition::class.java)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.platon42.intellij.plugins.m68k.utils
|
||||
|
||||
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.M68kAsmInstruction
|
||||
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) }
|
||||
}
|
||||
|
||||
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 {
|
||||
if (opSize == OP_UNSIZED && (adrMode.size != OP_UNSIZED)) {
|
||||
return if ((adrMode.size and OP_SIZE_W) == OP_SIZE_W) {
|
||||
|
||||
@@ -50,6 +50,12 @@
|
||||
<localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kSyntaxInspection"
|
||||
displayName="Assembly instruction validity" groupName="M68k"
|
||||
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>
|
||||
|
||||
<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>
|
||||
+46
-8
@@ -19,7 +19,47 @@ internal class M68kInstructionDocumentationProviderTest : AbstractDocumentationP
|
||||
)
|
||||
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>#<xxx></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>#<xxx></div>
|
||||
</td>
|
||||
<td valign="top">
|
||||
<div>Dn</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
"""
|
||||
)
|
||||
}
|
||||
@@ -39,15 +79,13 @@ internal class M68kInstructionDocumentationProviderTest : AbstractDocumentationP
|
||||
<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>
|
||||
<td class="section" valign="top">Mnemonic / CCs</td>
|
||||
<td class="section" valign="top">Operand</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<div>bra.s</div>
|
||||
<div>bra.b</div>
|
||||
<div>bra.w</div>
|
||||
<td valign="top">bra.s<br/>bra.b<br/>bra.w
|
||||
<hr/>
|
||||
Condition Codes: <br/>Not affected.
|
||||
</td>
|
||||
<td valign="top">
|
||||
<div>(xxx).w|l</div>
|
||||
|
||||
+88
-7
@@ -11,7 +11,7 @@ import org.junit.jupiter.api.extension.ExtendWith
|
||||
internal class M68kRegisterFlowDocumentationProviderTest : AbstractDocumentationProviderTest() {
|
||||
|
||||
@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(
|
||||
"documentme.asm", """
|
||||
label
|
||||
@@ -34,7 +34,7 @@ nextlabel
|
||||
assertThat(generateDocumentation(myFixture))
|
||||
.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">
|
||||
<tr>
|
||||
<td valign="top"> </td>
|
||||
@@ -44,7 +44,7 @@ nextlabel
|
||||
<tr>
|
||||
<td valign="top"> </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>
|
||||
<td valign="top"> </td>
|
||||
@@ -56,7 +56,7 @@ nextlabel
|
||||
<tr>
|
||||
<td valign="top"> </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>
|
||||
<td valign="top"> </td>
|
||||
@@ -66,7 +66,7 @@ nextlabel
|
||||
<tr>
|
||||
<td valign="top"> </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>
|
||||
<td valign="top">--></td>
|
||||
@@ -76,7 +76,7 @@ nextlabel
|
||||
<tr>
|
||||
<td valign="top"> </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>
|
||||
<td valign="top"> </td>
|
||||
@@ -96,7 +96,88 @@ nextlabel
|
||||
<td valign="top"> ; <font color="#00ff00">sets d0.l</font></td>
|
||||
</tr>
|
||||
</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"> </td>
|
||||
<td valign="top">
|
||||
<div class="grayed">[...]</div>
|
||||
</td>
|
||||
<td valign="top"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">--></td>
|
||||
<td valign="top"><b><code>move.w d0,<font color="#ffc800">d1</font></code></b></td>
|
||||
<td valign="top"> ; <--</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top"> </td>
|
||||
<td valign="top">
|
||||
<div class="grayed">[...]</div>
|
||||
</td>
|
||||
<td valign="top"> </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"> </td>
|
||||
<td valign="top">
|
||||
<div class="grayed">[...]</div>
|
||||
</td>
|
||||
<td valign="top"> </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)
|
||||
@TestDataPath("src/test/resources/inspections")
|
||||
abstract class AbstractInspectionTest : AbstractM68kTest() {
|
||||
|
||||
protected fun assertHighlightings(myFixture: CodeInsightTestFixture, count: Int, snippet: String) {
|
||||
assertThat(myFixture.doHighlighting())
|
||||
.areExactly(count, Condition({ it.description?.contains(snippet) ?: false }, "containing"))
|
||||
|
||||
+211
@@ -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")
|
||||
}
|
||||
}
|
||||
+74
@@ -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)")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user