23 Commits
Author SHA1 Message Date
chrisly42 62f7f13323 Bumped to IntelliJ/CLion 2021.2.2 2021-09-25 21:29:17 +02:00
chrisly42 c4051a0377 'END' detection was breaking parsing, causing havoc. 2021-09-25 17:20:45 +02:00
chrisly42 a03de6c394 Added documentation provider info for global labels. Shows directives and comments above.
Fixed BNF for labels with preprocessor statements.
Bumped versions.
2021-09-25 15:19:05 +02:00
chrisly42 1bff1a12c2 Documentation for instruction with special register shows specific register expected. 2021-09-05 18:42:20 +02:00
chrisly42 665947827b Updated build to latest versions. 2021-09-04 14:23:15 +02:00
chrisly42 3bb6165a58 Courtesy of Yann:
Bugfix: Special registers for address mode matching only worked with lower case register names.
Bugfix: movem with pc-relative mode was missing for weird immediate mode.
Bugfix: btst with pc-relative and weird immediate mode was missing.
Enhancement: Assembler syntax with implicit immediate 1 for shifts and rotations no longer cause syntax errors.
2021-09-04 14:08:10 +02:00
chrisly42 89bd964fe7 Minor updates to documentation. 2021-08-09 15:27:12 +02:00
chrisly42 0cb90ff8d7 'END' directive stops parsing. Prepared next release. 2021-08-09 12:14:02 +02:00
chrisly42 431caf64fd Added inspection for unresolved symbols, macros and labels. 2021-08-09 11:26:13 +02:00
chrisly42 a3f7ddb4f7 New: Added inspection suppression possibility and quickfix. 2021-08-09 10:08:41 +02:00
chrisly42 593719043e New: Code completion for local label definitions, suggesting undefined labels already referenced. 2021-08-08 13:40:01 +02:00
chrisly42 e0cdfef42b New: Files in 'include' directives can be referenced and renamed/refactored. 2021-08-06 20:03:47 +02:00
chrisly42 8d7977927f Added M68kIncludeFileProvider, but dunno what it's actually for. 2021-08-06 19:12:54 +02:00
chrisly42 ddf78ec210 Slightly reworked lexer to for a few directives:
'opt' and several other directives ('printt', 'fail' etc.) no longer causes a syntax error when unquoted.
'include', 'incdir' and 'incbin' and 'output' with '<pathname>' quotes no longer cause syntax error.
2021-08-06 10:06:48 +02:00
chrisly42 2c3daf28f7 TODO list. Prepared next release (for tomorrow). 2021-08-05 19:02:55 +02:00
chrisly42 1dcf288d27 Added inspection to warn about unexpected condition code unaffecting instructions before conditional instructions.
Extended documentation.
Bugfix in M68kDeadWriteInspection.
2021-08-05 18:42:32 +02:00
chrisly42 2abb5af8b0 Macros with register name now abort Dead Write analysis. Fix for modifying statements extending the with of the written data. 2021-08-05 16:21:32 +02:00
chrisly42 6c9a726b86 Preprocessor statements now abort Dead Write analysis. 2021-08-05 15:46:16 +02:00
chrisly42 921449cbb8 Refactored stuff.
Added control flow information to ISA.
In ISA exg is no longer treated as setting a definitive value.
Added inspection find dead writes to registers.
2021-08-04 17:39:54 +02:00
chrisly42 6f99c2ffcc Moved Condition-Code stuff to separate file, added reverse mapping function.
Reworked Instruction Documentation provider, now shows condition codes.
Cosmetics: Removed MutableList information where unnecessary
2021-08-04 12:24:16 +02:00
chrisly42 5881dcdaf8 Optimized mnemonic lookup. 2021-08-03 18:28:53 +02:00
chrisly42 71398f51d2 Reworked and extended ISA to hold condition code testing and affecting information (not used yet).
Minor fix for `andi/eori/ori to ccr` which were not byte sized in ISA.
Added alternate condition code tests HS (=CC) and LO (=CS).
2021-08-03 13:45:44 +02:00
chrisly42 eb26793a20 movem ISA was wrong regarding movem.w <ea>,<registerlist> (sign extends registers).
Changed Register Flow Documentation wording from 'reads' to 'uses' and from 'modifies' to 'changes'.
Updated screenshot. Added another test case for M68kRegisterFlowDocumentationProviderTest.
2021-08-03 10:57:53 +02:00
78 changed files with 2695 additions and 619 deletions
+129 -13
View File
@@ -2,7 +2,7 @@
_MC68000 Assembly Language Plugin_ is plugin for Jetbrains IDEs (CLion, IntelliJ, etc.).
![Example Syntax Highlighting](docs/syntaxhighlighting.png "Example Syntax Highlighting")
![Example IDE Screenshot](docs/example.png "Example IDE Screenshot")
## Purpose
@@ -10,8 +10,8 @@ This plugin delivers support for MC68000 assembly language files ([VAsm](http://
It adds a language parser with syntax highlighting, referencing and refactoring support, and a few more features.
I'm an Amiga retro democoder, and the lack of a plugin for M68k was the motivation to write one. Also, diving deep into custom language plugins has a steep
learning curve.
I'm an Amiga retro democoder (among other things), and the lack of a plugin for M68k was the motivation to write one. Also, diving deep into custom language
plugins has a steep learning curve.
When I started the plugin in July 2021, I was not aware of the [M68k plugin efforts by Jetbrains employee Yann Cébron](https://github.com/YannCebron/m68kplugin)
who has been working on the same topic for quite some time. At the time of writing, his plugin however, has not been release yet. Nevertheless, it has a lot of
@@ -19,17 +19,17 @@ 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 the first versions 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.
- Validates the assembly syntax against 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
- References / Refactoring support for local and global labels, symbol assignments, and macros.
- Symbols / labels / macros code completion
- References / refactoring support for local and global labels, symbol assignments, and macros.
- Simple register usage flow (hover over register or press F1 for full flow)
- Brace matching
- Quote handler
@@ -37,22 +37,98 @@ 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
The plugin provides a few inspections for code analysis. An error or warning can be suppressed by placing a `; suppress <InspectionName>` comment either on an
end of line comment behind the statement or in a full line comment above the statement.
#### M68kSyntax - 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)_)
#### M68kDeadWrite - 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`.
#### M68kUnexpectedConditionalInstruction - 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.
#### M68kUnresolvedReference - Unresolved label/symbol/macro reference
Points out unresolved references such for global and local labels, macros or symbols. Right now, missing symbol and global label references are shown only as
weak warnings as missing macro evaluation will not resolve symbols defined via `STRUCT` macros.
### Documentation provider
#### M68kSymbolDefinition
Provides the assigned value of a `=`, `set` or `equ` symbol definition when hovering over a symbol.
#### M68kGlobalLabel
Shows the comments above the label and if the first statement after the label is a directive like `include` or `dc.b`, shows it, too.
#### M68kRegisterFlow
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).
#### M68kInstruction
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 (?)
- `END` detection was breaking parsing, so this is disabled for now until I find a working solution.
- Macro invocations are not yet evaluated, thus no referencing to symbols defined via macros (e.g. `STRUCT`).
- No support for includes. Scoping is for global symbols and labels is currently the whole project.
- Scoping for global symbols and labels is currently the whole project.
- No support for register replacement (e.g. registers replaced by `EQUR` or `EQURL` will cause syntax errors)
- While the Lexer supports the -spaces option (where a space introduces a comment), this cannot be configured yet (default is off).
- No support for other processor instructions, FPU or 68020+ address modes.
- Unit Test coverage is not as good as it could be (ahem).
- `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 +145,48 @@ 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).
## Feedback
I guess there are currently over 500 users of this plugin and while I wrote this mainly for myself, I'm only doing this in my spare time.
Feedback and [rating](https://plugins.jetbrains.com/plugin/17268-mc68000-assembly-language-support/reviews)
are appreciated. It really is keeping me motivated to continue development.
## Changelog
### V0.7 (26-Sep-21)
- Bugfix: `btst` with pc-relative and weird immediate mode was missing (courtesy of Yann).
- Bugfix: `movem` with pc-relative mode was missing for weird immediate mode (courtesy of Yann).
- Bugfix: Special registers for address mode matching only worked with lower case register names (courtesy of Yann).
- Enhancement: Assembler syntax with implicit immediate 1 for shifts and rotations no longer cause syntax errors (courtesy of Yann).
- Enhancement: Documentation for instruction with special register shows specific register expected.
- New: Added documentation provider info for global labels. Shows directives and comments above.
- Bugfix: Fixed BNF for labels with preprocessor statements.
- Disabled: `END` detection was breaking parsing, causing havoc.
### V0.6 (09-Aug-21)
- Enhancement: `opt` and several other directives (`printt`, `fail` etc.) no longer causes a syntax error when unquoted.
- Enhancement: `include`, `incdir` and `incbin` and `output` with `<pathname>` quotes no longer cause syntax error.
- New: Files in `include` directives can be referenced and renamed/refactored.
- New: Code completion for local label definitions, suggesting undefined labels already referenced.
- New: Added inspection suppression possibility and quickfix.
- New: Added inspection for unresolved symbols, macros and labels.
- Enhancement: 'END' directive stops parsing.
### 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.
@@ -90,7 +206,7 @@ make it work with JUnit 5. Feel free to use the code (in package ```de.platon42.
- Enhancement: Macro definitions are now word and stub indexed, macro calls reference to definition.
- New: Macro definition refactoring and find usages support.
- Enhancement: Structural View also shows macro definitions.
- Bugfix: Missing REPT and ENDR assembler directives added.
- Bugfix: Missing `REPT` and `ENDR` assembler directives added.
- Cosmetics: Changed or added some icons at various places.
- Performance: Reference search for global labels and symbols now uses stub index.
- Compatibility: Restored compatibility with IDE versions < 2021.1.
+20 -41
View File
@@ -1,13 +1,13 @@
plugins {
id 'java'
id 'org.jetbrains.intellij' version '1.1.3'
id 'org.jetbrains.intellij' version '1.1.6'
id 'org.jetbrains.kotlin.jvm' version '1.5.21'
id 'jacoco'
id 'com.github.kt3k.coveralls' version '2.11.0'
id 'com.github.kt3k.coveralls' version '2.12.0'
}
group = 'de.platon42'
version = '0.4'
version = '0.7'
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
@@ -22,9 +22,9 @@ repositories {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
testImplementation "org.assertj:assertj-core:3.20.2"
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.0-M1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.0-M1'
testImplementation "org.assertj:assertj-core:3.21.0"
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
testImplementation "org.jetbrains.kotlin:kotlin-test"
testImplementation "org.jetbrains.kotlin:kotlin-reflect"
// testImplementation "org.jetbrains.kotlin:kotlin-test-junit"
@@ -42,53 +42,32 @@ compileTestKotlin {
}
intellij {
setVersion("2021.2") // LATEST-EAP-SNAPSHOT
setVersion("2021.2.2") // LATEST-EAP-SNAPSHOT
setUpdateSinceUntilBuild(false)
// setPlugins(["com.intellij.java"])
}
runPluginVerifier {
ideVersions = ["IC-203.6682.168", "IC-212.4746.92", // 2020.3 - 2021.2
ideVersions = ["IC-203.6682.168", "IC-212.5284.40", // 2020.3 - 2021.2.2
"CL-203.5981.166", "CL-203.8084.11", // 2020.3
"CL-211.6693.114", "CL-211.7628.27", // 2021.1
"CL-212.4746.93"] // 2021.2
downloadDir = System.getProperty("user.home") + "/.gradle/caches/modules-2/files-2.1/com.jetbrains.intellij.idea"
"CL-212.4746.93", "CL-212.5284.51"] // 2021.2 - 2021.2.2
downloadDir = System.getProperty("user.home") + "/.gradle/caches/modules-2/files-2.1/com.jetbrains.intellij.idea/verifier"
}
patchPluginXml {
setChangeNotes("""
<h4>V0.4 (03-Aug-21)</h4>
<p>I still got zero feedback and zero <a href="https://plugins.jetbrains.com/plugin/17268-mc68000-assembly-language-support/reviews">ratings</a> :-(</p>
<h4>V0.7 (26-Sep-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.
<li>Enhancement: Added Structure View filters.
<li>New: Added inspection to validate the correctness of a MC68000 instruction regarding operation size and address modes.
<li>Bugfix: Added several missing assembler directives (opt, machine, etc.).
<li>Bugfix: Uppercase hexadecimal literals were not parsed (JFlex bug?).
<li>Bugfix: Interpretation of register lists was wrong in BNF.
<li>New: Added Documentation Provider for symbol definitions (shows assigned declaration).
<li>New: Added Documentation Provider for mnemonics (simple version, generated out of ISA information).
<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.
<li>Bugfix: 'btst' with pc-relative and weird immediate mode was missing (courtesy of Yann).
<li>Bugfix: 'movem' with pc-relative mode was missing for weird immediate mode (courtesy of Yann).
<li>Bugfix: Special registers for address mode matching only worked with lower case register names (courtesy of Yann).
<li>Enhancement: Assembler syntax with implicit immediate 1 for shifts and rotations no longer cause syntax errors (courtesy of Yann).
<li>Enhancement: Documentation for instruction with special register shows specific register expected.
<li>New: Added documentation provider info for global labels. Shows directives and comments above.
<li>Bugfix: Fixed BNF for labels with preprocessor statements.
<li>Disabled: 'END' detection was breaking parsing, causing havoc.
</ul>
<p>Full changelog available at <a href="https://github.com/chrisly42/mc68000-asm-plugin#changelog">Github project site</a>.</p>
""")
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

@@ -40,7 +40,7 @@ public class _M68kLexer implements FlexLexer {
public static final int ASSIGNMENT = 12;
public static final int EXPR = 14;
public static final int EXPR_OP = 16;
public static final int MACROCALL = 18;
public static final int PLAINPARAMS = 18;
public static final int WAITEOL = 20;
public static final int MACRODEF = 22;
public static final int MACROLINE = 24;
@@ -194,23 +194,23 @@ public class _M68kLexer implements FlexLexer {
private static final int[] ZZ_ACTION = zzUnpackAction();
private static final String ZZ_ACTION_PACKED_0 =
"\17\0\1\1\2\2\1\3\1\4\1\1\1\5\1\6" +
"\1\7\1\10\1\1\1\7\2\11\1\7\1\10\1\12" +
"\1\13\1\14\1\1\1\15\1\16\1\17\4\16\1\1" +
"\1\16\5\1\1\20\1\21\1\22\1\23\1\24\1\25" +
"\1\26\1\27\1\30\1\31\2\16\1\32\1\33\1\34" +
"\1\35\1\36\1\37\1\40\1\41\1\42\1\43\1\44" +
"\1\45\1\46\1\47\1\42\1\50\2\1\1\51\1\16" +
"\1\52\1\11\4\52\1\53\1\11\2\54\2\55\1\56" +
"\1\57\3\60\1\57\1\60\1\0\1\4\1\61\2\62" +
"\1\63\3\0\1\7\1\64\1\65\1\66\1\67\1\70" +
"\1\71\1\16\1\72\1\73\1\16\1\74\1\75\2\0" +
"\1\76\4\0\1\77\1\16\1\32\1\100\1\101\1\102" +
"\1\103\1\104\1\105\1\106\3\0\1\52\1\0\1\52" +
"\1\0\1\52\1\0\1\52\1\55\1\57\3\0\1\4" +
"\1\62\1\63\1\7\1\107\1\7\1\110\1\111\1\112" +
"\1\113\1\0\1\52\3\0\1\55\1\57\3\0\1\7" +
"\1\55\1\57\2\0\1\114\1\115\1\116\1\0\1\117";
"\17\0\1\1\2\2\1\3\1\4\1\5\1\6\1\7" +
"\1\10\1\11\1\1\1\10\2\12\1\10\1\11\1\13" +
"\1\14\1\15\1\1\1\16\1\17\1\20\4\17\1\1" +
"\1\17\5\1\1\21\1\22\1\23\1\24\1\25\1\26" +
"\1\27\1\30\1\31\1\32\2\17\1\33\1\34\1\35" +
"\1\36\1\37\1\40\1\41\1\42\1\43\1\44\1\45" +
"\1\46\1\47\1\50\1\43\1\51\2\1\1\52\1\17" +
"\1\53\1\12\4\53\1\54\1\12\2\55\2\56\1\57" +
"\1\60\3\61\1\60\1\61\1\0\1\4\1\62\2\63" +
"\1\64\3\0\1\10\1\65\1\66\1\67\1\70\1\71" +
"\1\72\1\17\1\73\1\74\1\17\1\75\1\76\2\0" +
"\1\77\4\0\1\100\1\17\1\33\1\101\1\102\1\103" +
"\1\104\1\105\1\106\1\107\3\0\1\53\1\0\1\53" +
"\1\0\1\53\1\0\1\53\1\56\1\60\3\0\1\4" +
"\1\63\1\64\1\10\1\110\1\10\1\111\1\112\1\113" +
"\1\114\1\0\1\53\3\0\1\56\1\60\3\0\1\10" +
"\1\56\1\60\2\0\1\115\1\116\1\117\1\0\1\120";
private static int[] zzUnpackAction() {
int[] result = new int[184];
@@ -624,11 +624,11 @@ public class _M68kLexer implements FlexLexer {
/**
* Refills the input buffer.
*
* @return {@code false}, iff there was new input.
* @return {@code false}, iff there was NO new input.
* @throws java.io.IOException if any I/O-Error occurs
*/
private boolean zzRefill() throws java.io.IOException {
return true;
return !zzAtEOF;
}
@@ -814,14 +814,14 @@ public class _M68kLexer implements FlexLexer {
return BAD_CHARACTER;
}
// fall through
case 80:
case 81:
break;
case 2: {
yybegin(YYINITIAL);
return WHITE_SPACE;
}
// fall through
case 81:
case 82:
break;
case 3: {
yybegin(NOSOL);
@@ -829,7 +829,7 @@ public class _M68kLexer implements FlexLexer {
return WHITE_SPACE;
}
// fall through
case 82:
case 83:
break;
case 4: {
yybegin(INSTRPART);
@@ -838,22 +838,30 @@ public class _M68kLexer implements FlexLexer {
return GLOBAL_LABEL_DEF;
}
// fall through
case 83:
break;
case 5: {
yybegin(YYINITIAL);
return COMMENT;
}
// fall through
case 84:
break;
case 6: {
return WHITE_SPACE;
case 5: {
yybegin(INSTRPART);
eatOneWhitespace = false;
return LOCAL_LABEL_DEF;
}
// fall through
case 85:
break;
case 6: {
yybegin(YYINITIAL);
return COMMENT;
}
// fall through
case 86:
break;
case 7: {
return WHITE_SPACE;
}
// fall through
case 87:
break;
case 8: {
if (isAsmMnemonicWithSize(yytext())) {
yybegin(ASMINSTR);
yypushback(2);
@@ -863,10 +871,15 @@ public class _M68kLexer implements FlexLexer {
yybegin(ASMINSTR);
return MNEMONIC;
}
//if(isEndDirective(yytext())) { yybegin(YYINITIAL); zzAtEOF = true; return null; }
if (isDataDirective(yytext())) {
startExpr(EXPR, EXPR_OP);
return DATA_DIRECTIVE;
}
if (isPlainDirective(yytext())) {
yybegin(PLAINPARAMS);
return OTHER_DIRECTIVE;
}
if (isOtherDirective(yytext())) {
startExpr(EXPR, EXPR_OP);
return OTHER_DIRECTIVE;
@@ -874,498 +887,498 @@ public class _M68kLexer implements FlexLexer {
return handleMacroMode(this);
}
// fall through
case 86:
break;
case 8: {
return handleMacroMode(this);
}
// fall through
case 87:
break;
case 9: {
yybegin(YYINITIAL);
return EOL;
}
// fall through
case 88:
break;
case 10: {
return COLON;
case 9: {
return handleMacroMode(this);
}
// fall through
case 89:
break;
case 11: {
yybegin(WAITEOL);
return COMMENT;
case 10: {
yybegin(YYINITIAL);
return EOL;
}
// fall through
case 90:
break;
case 12: {
startExpr(ASMOPS, ASMOPS_OP);
return WHITE_SPACE;
case 11: {
return COLON;
}
// fall through
case 91:
break;
case 13: {
return handleEolCommentWhitespace(this);
case 12: {
yybegin(WAITEOL);
return COMMENT;
}
// fall through
case 92:
break;
case 14: {
yybegin(exprOpState);
return SYMBOL;
case 13: {
startExpr(ASMOPS, ASMOPS_OP);
return WHITE_SPACE;
}
// fall through
case 93:
break;
case 15: {
yybegin(exprOpState);
return DECIMAL;
case 14: {
return handleEolCommentWhitespace(this);
}
// fall through
case 94:
break;
case 16: {
return SEPARATOR;
case 15: {
yybegin(exprOpState);
return SYMBOL;
}
// fall through
case 95:
break;
case 17: {
return HASH;
case 16: {
yybegin(exprOpState);
return DECIMAL;
}
// fall through
case 96:
break;
case 18: {
return OP_BITWISE_XOR;
case 17: {
return SEPARATOR;
}
// fall through
case 97:
break;
case 19: {
return ROUND_L;
case 18: {
return HASH;
}
// fall through
case 98:
break;
case 20: {
yybegin(exprOpState);
return ROUND_R;
case 19: {
return OP_BITWISE_XOR;
}
// fall through
case 99:
break;
case 21: {
return OP_UNARY_NOT;
case 20: {
return ROUND_L;
}
// fall through
case 100:
break;
case 22: {
return OP_UNARY_COMPL;
case 21: {
yybegin(exprOpState);
return ROUND_R;
}
// fall through
case 101:
break;
case 23: {
return OP_PLUS;
case 22: {
return OP_UNARY_NOT;
}
// fall through
case 102:
break;
case 24: {
return OP_MINUS;
case 23: {
return OP_UNARY_COMPL;
}
// fall through
case 103:
break;
case 25: {
yybegin(exprOpState);
return CURRENT_PC_SYMBOL;
case 24: {
return OP_PLUS;
}
// fall through
case 104:
break;
case 26: {
yybegin(exprState);
return OP_CMP_EQ;
case 25: {
return OP_MINUS;
}
// fall through
case 105:
break;
case 27: {
yybegin(exprState);
return OP_AR_MOD;
case 26: {
yybegin(exprOpState);
return CURRENT_PC_SYMBOL;
}
// fall through
case 106:
break;
case 28: {
case 27: {
yybegin(exprState);
return OP_CMP_LT;
return OP_CMP_EQ;
}
// fall through
case 107:
break;
case 29: {
case 28: {
yybegin(exprState);
return OP_CMP_GT;
return OP_AR_MOD;
}
// fall through
case 108:
break;
case 30: {
case 29: {
yybegin(exprState);
return SEPARATOR;
return OP_CMP_LT;
}
// fall through
case 109:
break;
case 31: {
case 30: {
yybegin(exprState);
return OP_BITWISE_XOR;
return OP_CMP_GT;
}
// fall through
case 110:
break;
case 32: {
case 31: {
yybegin(exprState);
return ROUND_L;
return SEPARATOR;
}
// fall through
case 111:
break;
case 33: {
return ROUND_R;
case 32: {
yybegin(exprState);
return OP_BITWISE_XOR;
}
// fall through
case 112:
break;
case 34: {
case 33: {
yybegin(exprState);
return OP_BITWISE_OR;
return ROUND_L;
}
// fall through
case 113:
break;
case 35: {
yybegin(exprState);
return OP_UNARY_COMPL;
case 34: {
return ROUND_R;
}
// fall through
case 114:
break;
case 36: {
case 35: {
yybegin(exprState);
return OP_PLUS;
return OP_BITWISE_OR;
}
// fall through
case 115:
break;
case 37: {
case 36: {
yybegin(exprState);
return OP_MINUS;
return OP_UNARY_COMPL;
}
// fall through
case 116:
break;
case 38: {
case 37: {
yybegin(exprState);
return OP_AR_MUL;
return OP_PLUS;
}
// fall through
case 117:
break;
case 39: {
case 38: {
yybegin(exprState);
return OP_BITWISE_AND;
return OP_MINUS;
}
// fall through
case 118:
break;
case 40: {
case 39: {
yybegin(exprState);
return OP_AR_DIV;
return OP_AR_MUL;
}
// fall through
case 119:
break;
case 41: {
startExpr(EXPR, EXPR_OP);
return OP_ASSIGN;
case 40: {
yybegin(exprState);
return OP_BITWISE_AND;
}
// fall through
case 120:
break;
case 42: {
return STRINGLIT;
case 41: {
yybegin(exprState);
return OP_AR_DIV;
}
// fall through
case 121:
break;
case 43: {
return COMMENT;
case 42: {
startExpr(EXPR, EXPR_OP);
return OP_ASSIGN;
}
// fall through
case 122:
break;
case 43: {
return STRINGLIT;
}
// fall through
case 123:
break;
case 44: {
return COMMENT;
}
// fall through
case 124:
break;
case 45: {
yybegin(MACROLINE);
macroLines = 0;
return WHITE_SPACE;
}
// fall through
case 123:
break;
case 45: {
return MACRO_NAME;
}
// fall through
case 124:
break;
case 46: {
yybegin(MACROWAITEOL);
return COMMENT;
}
// fall through
case 125:
break;
case 47: {
return MACRO_LINE;
case 46: {
return MACRO_NAME;
}
// fall through
case 126:
break;
case 48: {
return handleMacroLineEol(this);
case 47: {
yybegin(MACROWAITEOL);
return COMMENT;
}
// fall through
case 127:
break;
case 48: {
return MACRO_LINE;
}
// fall through
case 128:
break;
case 49: {
return handleMacroLineEol(this);
}
// fall through
case 129:
break;
case 50: {
yybegin(ASSIGNMENT);
eatOneWhitespace = true;
yypushback(pushbackAssignment(yytext()));
return SYMBOLDEF;
}
// fall through
case 128:
case 130:
break;
case 50: {
case 51: {
yybegin(INSTRPART);
eatOneWhitespace = false;
yypushback(pushbackLabelColons(yytext()));
return LOCAL_LABEL_DEF;
}
// fall through
case 129:
case 131:
break;
case 51: {
case 52: {
yybegin(INSTRPART);
yypushback(pushbackLabelColons(yytext()));
return GLOBAL_LABEL_DEF;
}
// fall through
case 130:
break;
case 52: {
return OPSIZE_BS;
}
// fall through
case 131:
break;
case 53: {
return OPSIZE_W;
}
// fall through
case 132:
break;
case 54: {
return OPSIZE_L;
case 53: {
return OPSIZE_BS;
}
// fall through
case 133:
break;
case 55: {
yybegin(exprOpState);
return AREG;
case 54: {
return OPSIZE_W;
}
// fall through
case 134:
break;
case 56: {
yybegin(exprOpState);
return REG_SP;
case 55: {
return OPSIZE_L;
}
// fall through
case 135:
break;
case 57: {
case 56: {
yybegin(exprOpState);
return DREG;
return AREG;
}
// fall through
case 136:
break;
case 58: {
case 57: {
yybegin(exprOpState);
return REG_SR;
return REG_SP;
}
// fall through
case 137:
break;
case 59: {
case 58: {
yybegin(exprOpState);
return HEXADECIMAL;
return DREG;
}
// fall through
case 138:
break;
case 60: {
case 59: {
yybegin(exprOpState);
return BINARY;
return REG_SR;
}
// fall through
case 139:
break;
case 61: {
case 60: {
yybegin(exprOpState);
return OCTAL;
return HEXADECIMAL;
}
// fall through
case 140:
break;
case 62: {
case 61: {
yybegin(exprOpState);
return STRINGLIT;
return BINARY;
}
// fall through
case 141:
break;
case 63: {
case 62: {
yybegin(exprOpState);
return PC;
return OCTAL;
}
// fall through
case 142:
break;
case 64: {
yybegin(exprState);
return OP_CMP_LT_EQ;
case 63: {
yybegin(exprOpState);
return STRINGLIT;
}
// fall through
case 143:
break;
case 65: {
yybegin(exprState);
return OP_AR_SHIFT_L;
case 64: {
yybegin(exprOpState);
return PC;
}
// fall through
case 144:
break;
case 66: {
case 65: {
yybegin(exprState);
return OP_CMP_NOT_EQ;
return OP_CMP_LT_EQ;
}
// fall through
case 145:
break;
case 67: {
case 66: {
yybegin(exprState);
return OP_CMP_GT_EQ;
return OP_AR_SHIFT_L;
}
// fall through
case 146:
break;
case 68: {
case 67: {
yybegin(exprState);
return OP_AR_SHIFT_R;
return OP_CMP_NOT_EQ;
}
// fall through
case 147:
break;
case 69: {
case 68: {
yybegin(exprState);
return OP_LOGICAL_AND;
return OP_CMP_GT_EQ;
}
// fall through
case 148:
break;
case 70: {
case 69: {
yybegin(exprState);
return OP_LOGICAL_OR;
return OP_AR_SHIFT_R;
}
// fall through
case 149:
break;
case 70: {
yybegin(exprState);
return OP_LOGICAL_AND;
}
// fall through
case 150:
break;
case 71: {
yybegin(exprState);
return OP_LOGICAL_OR;
}
// fall through
case 151:
break;
case 72: {
yybegin(INSTRPART);
yypushback(pushbackLabelColons(yytext()));
return LOCAL_LABEL_DEF;
}
// fall through
case 150:
case 152:
break;
case 72: {
case 73: {
yybegin(exprOpState);
return REG_USP;
}
// fall through
case 151:
case 153:
break;
case 73: {
case 74: {
yybegin(exprOpState);
return REG_CCR;
}
// fall through
case 152:
case 154:
break;
case 74: {
case 75: {
yybegin(exprOpState);
return REG_VBR;
}
// fall through
case 153:
case 155:
break;
case 75: {
case 76: {
startExpr(EXPR, EXPR_OP);
return EQU;
}
// fall through
case 154:
case 156:
break;
case 76: {
case 77: {
yybegin(MACRODEF);
return MACRO_TAG;
}
// fall through
case 155:
case 157:
break;
case 77: {
case 78: {
return MACRO_TAG;
}
// fall through
case 156:
case 158:
break;
case 78: {
case 79: {
yybegin(MACROTERMINATION);
return MACRO_END_TAG;
}
// fall through
case 157:
case 159:
break;
case 79: {
case 80: {
yybegin(MACRODEF);
yypushback(pushbackAfterFirstToken(yytext()));
return MACRO_NAME;
}
// fall through
case 158:
case 160:
break;
default:
zzScanError(ZZ_NO_MATCH);
@@ -562,9 +562,13 @@ public class M68kParser implements PsiParser, LightPsiParser {
}
/* ********************************************************** */
// Instruction
// Instruction|PreprocessorDirective
static boolean InstructionOnly(PsiBuilder b, int l) {
return Instruction(b, l + 1);
if (!recursion_guard_(b, l, "InstructionOnly")) return false;
boolean r;
r = Instruction(b, l + 1);
if (!r) r = PreprocessorDirective(b, l + 1);
return r;
}
/* ********************************************************** */
@@ -596,18 +600,27 @@ public class M68kParser implements PsiParser, LightPsiParser {
}
/* ********************************************************** */
// Label Instruction
// Label (Instruction|PreprocessorDirective)
static boolean LabelWithInstruction(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "LabelWithInstruction")) return false;
if (!nextTokenIs(b, "", GLOBAL_LABEL_DEF, LOCAL_LABEL_DEF)) return false;
boolean r;
Marker m = enter_section_(b);
r = Label(b, l + 1);
r = r && Instruction(b, l + 1);
r = r && LabelWithInstruction_1(b, l + 1);
exit_section_(b, m, null, r);
return r;
}
// Instruction|PreprocessorDirective
private static boolean LabelWithInstruction_1(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "LabelWithInstruction_1")) return false;
boolean r;
r = Instruction(b, l + 1);
if (!r) r = PreprocessorDirective(b, l + 1);
return r;
}
/* ********************************************************** */
// LOCAL_LABEL_DEF COLON?
public static boolean LocalLabel(PsiBuilder b, int l) {
@@ -817,42 +830,38 @@ public class M68kParser implements PsiParser, LightPsiParser {
}
/* ********************************************************** */
// Label? (DATA_DIRECTIVE | OTHER_DIRECTIVE)
// PreprocessorOperands?
// PreprocessorKeyword PreprocessorOperands?
public static boolean PreprocessorDirective(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "PreprocessorDirective")) return false;
if (!nextTokenIs(b, "<preprocessor directive>", DATA_DIRECTIVE, OTHER_DIRECTIVE)) return false;
boolean r;
Marker m = enter_section_(b, l, _NONE_, PREPROCESSOR_DIRECTIVE, "<preprocessor directive>");
r = PreprocessorDirective_0(b, l + 1);
r = PreprocessorKeyword(b, l + 1);
r = r && PreprocessorDirective_1(b, l + 1);
r = r && PreprocessorDirective_2(b, l + 1);
exit_section_(b, l, m, r, false, null);
return r;
}
// Label?
private static boolean PreprocessorDirective_0(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "PreprocessorDirective_0")) return false;
Label(b, l + 1);
return true;
}
// DATA_DIRECTIVE | OTHER_DIRECTIVE
// PreprocessorOperands?
private static boolean PreprocessorDirective_1(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "PreprocessorDirective_1")) return false;
boolean r;
r = consumeToken(b, DATA_DIRECTIVE);
if (!r) r = consumeToken(b, OTHER_DIRECTIVE);
return r;
}
// PreprocessorOperands?
private static boolean PreprocessorDirective_2(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "PreprocessorDirective_2")) return false;
PreprocessorOperands(b, l + 1);
return true;
}
/* ********************************************************** */
// DATA_DIRECTIVE | OTHER_DIRECTIVE
public static boolean PreprocessorKeyword(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "PreprocessorKeyword")) return false;
if (!nextTokenIs(b, "<preprocessor keyword>", DATA_DIRECTIVE, OTHER_DIRECTIVE)) return false;
boolean r;
Marker m = enter_section_(b, l, _NONE_, PREPROCESSOR_KEYWORD, "<preprocessor keyword>");
r = consumeToken(b, DATA_DIRECTIVE);
if (!r) r = consumeToken(b, OTHER_DIRECTIVE);
exit_section_(b, l, m, r, false, null);
return r;
}
/* ********************************************************** */
// expr
static boolean PreprocessorOperand(PsiBuilder b, int l) {
@@ -1217,14 +1226,12 @@ public class M68kParser implements PsiParser, LightPsiParser {
/* ********************************************************** */
// Assignment
// | PreprocessorDirective
// | LabelInsts
public static boolean statement(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "statement")) return false;
boolean r;
Marker m = enter_section_(b, l, _NONE_, STATEMENT, "<statement>");
r = Assignment(b, l + 1);
if (!r) r = PreprocessorDirective(b, l + 1);
if (!r) r = LabelInsts(b, l + 1);
exit_section_(b, l, m, r, false, M68kParser::statement_recover);
return r;
@@ -1236,11 +1243,21 @@ public class M68kParser implements PsiParser, LightPsiParser {
if (!recursion_guard_(b, l, "statement_recover")) return false;
boolean r;
Marker m = enter_section_(b, l, _NOT_);
r = !consumeToken(b, EOL);
r = !statement_recover_0(b, l + 1);
exit_section_(b, l, m, r, false, null);
return r;
}
// (EOL)
private static boolean statement_recover_0(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "statement_recover_0")) return false;
boolean r;
Marker m = enter_section_(b);
r = consumeTokenFast(b, EOL);
exit_section_(b, m, null, r);
return r;
}
/* ********************************************************** */
// Expression root: expr
// Operator priority table:
@@ -2,17 +2,13 @@
package de.platon42.intellij.plugins.m68k.psi;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public interface M68kPreprocessorDirective extends M68kPsiElement {
@Nullable
M68kGlobalLabel getGlobalLabel();
@Nullable
M68kLocalLabel getLocalLabel();
@NotNull
M68kPreprocessorKeyword getPreprocessorKeyword();
@NotNull
List<M68kExpr> getExprList();
@@ -0,0 +1,6 @@
// This is a generated file. Not intended for manual editing.
package de.platon42.intellij.plugins.m68k.psi;
public interface M68kPreprocessorKeyword extends M68kPsiElement {
}
@@ -58,6 +58,7 @@ public interface M68kTypes {
IElementType OPERAND_SIZE = new M68kElementType("OPERAND_SIZE");
IElementType PAREN_EXPR = new M68kElementType("PAREN_EXPR");
IElementType PREPROCESSOR_DIRECTIVE = new M68kElementType("PREPROCESSOR_DIRECTIVE");
IElementType PREPROCESSOR_KEYWORD = new M68kElementType("PREPROCESSOR_KEYWORD");
IElementType PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_NEW_ADDRESSING_MODE = new M68kElementType("PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_NEW_ADDRESSING_MODE");
IElementType PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_OLD_ADDRESSING_MODE = new M68kElementType("PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_OLD_ADDRESSING_MODE");
IElementType PROGRAM_COUNTER_INDIRECT_WITH_INDEX_NEW_ADDRESSING_MODE = new M68kElementType("PROGRAM_COUNTER_INDIRECT_WITH_INDEX_NEW_ADDRESSING_MODE");
@@ -233,6 +234,8 @@ public interface M68kTypes {
return new M68kParenExprImpl(node);
} else if (type == PREPROCESSOR_DIRECTIVE) {
return new M68kPreprocessorDirectiveImpl(node);
} else if (type == PREPROCESSOR_KEYWORD) {
return new M68kPreprocessorKeywordImpl(node);
} else if (type == PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_NEW_ADDRESSING_MODE) {
return new M68kProgramCounterIndirectWithDisplacementNewAddressingModeImpl(node);
} else if (type == PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_OLD_ADDRESSING_MODE) {
@@ -131,6 +131,10 @@ public class M68kVisitor extends PsiElementVisitor {
visitPsiElement(o);
}
public void visitPreprocessorKeyword(@NotNull M68kPreprocessorKeyword o) {
visitPsiElement(o);
}
public void visitProgramCounterIndirectWithDisplacementNewAddressingMode(@NotNull M68kProgramCounterIndirectWithDisplacementNewAddressingMode o) {
visitAddressingMode(o);
// visitWithDisplacement(o);
@@ -1,17 +1,15 @@
// This is a generated file. Not intended for manual editing.
package de.platon42.intellij.plugins.m68k.psi.impl;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.util.PsiTreeUtil;
import de.platon42.intellij.plugins.m68k.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class M68kPreprocessorDirectiveImpl extends ASTWrapperPsiElement implements M68kPreprocessorDirective {
public class M68kPreprocessorDirectiveImpl extends M68kPreprocessorDirectiveMixin implements M68kPreprocessorDirective {
public M68kPreprocessorDirectiveImpl(@NotNull ASTNode node) {
super(node);
@@ -28,15 +26,9 @@ public class M68kPreprocessorDirectiveImpl extends ASTWrapperPsiElement implemen
}
@Override
@Nullable
public M68kGlobalLabel getGlobalLabel() {
return PsiTreeUtil.getChildOfType(this, M68kGlobalLabel.class);
}
@Override
@Nullable
public M68kLocalLabel getLocalLabel() {
return PsiTreeUtil.getChildOfType(this, M68kLocalLabel.class);
@NotNull
public M68kPreprocessorKeyword getPreprocessorKeyword() {
return notNullChild(PsiTreeUtil.getChildOfType(this, M68kPreprocessorKeyword.class));
}
@Override
@@ -0,0 +1,27 @@
// This is a generated file. Not intended for manual editing.
package de.platon42.intellij.plugins.m68k.psi.impl;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElementVisitor;
import de.platon42.intellij.plugins.m68k.psi.M68kPreprocessorKeyword;
import de.platon42.intellij.plugins.m68k.psi.M68kVisitor;
import org.jetbrains.annotations.NotNull;
public class M68kPreprocessorKeywordImpl extends ASTWrapperPsiElement implements M68kPreprocessorKeyword {
public M68kPreprocessorKeywordImpl(@NotNull ASTNode node) {
super(node);
}
public void accept(@NotNull M68kVisitor visitor) {
visitor.visitPreprocessorKeyword(this);
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof M68kVisitor) accept((M68kVisitor) visitor);
else super.accept(visitor);
}
}
@@ -10,7 +10,7 @@ class M68kFileElementType private constructor() : ILightStubFileElementType<PsiF
@JvmField
val INSTANCE = M68kFileElementType()
const val STUB_VERSION = 5
const val STUB_VERSION = 6
const val STUB_EXTERNAL_ID_PREFIX = "MC68000."
const val EXTERNAL_ID = STUB_EXTERNAL_ID_PREFIX + "FILE"
}
@@ -22,7 +22,15 @@ object AssemblerDirectives {
"rsset", "clrfo", "clrso", "setfo", "setso"
)
val otherDirective: Set<String> = setOf(
val plainDirectives: Set<String> = setOf(
"incdir", "include", "incbin", "output", "idnt",
"printt", "echo", "fail",
"opt"
)
val otherDirectives: Set<String> = setOf(
"if",
"ifeq", "ifne", "ifgt", "ifge", "iflt", "ifle", "ifb", "ifnb", "ifc", "ifnc",
"ifd", "ifnd", "ifmacrod", "ifmacrond",
@@ -38,12 +46,10 @@ object AssemblerDirectives {
"reg", "equr", "equrl",
"freg", "fequr", "fequrl",
"incdir", "include", "incbin", "output",
"list", "nlist", "nolist", "llen", "nopage", "page", "spc",
"org",
"assert", "fail", "print", "printt", "printv", "echo",
"assert", "printv",
"inline", "einline",
"rem", "erem",
@@ -53,6 +59,6 @@ object AssemblerDirectives {
"basereg", "endb", "far", "near", "initnear",
"opt"
"end"
)
}
@@ -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()]!!
}
}
}
@@ -44,7 +44,7 @@ enum class AddressMode(val description: String, val syntax: String) {
PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT("program counter indirect with displacement", "(d16,PC)"),
ADDRESS_REGISTER_INDIRECT_WITH_INDEX("address register indirect with index", "(d8,An,Xn)"),
PROGRAM_COUNTER_INDIRECT_WITH_INDEX("program counter indirect with index", "(d8,PC,Xn)"),
SPECIAL_REGISTER_DIRECT("special register", "ccr|usp|vbr"),
SPECIAL_REGISTER_DIRECT("special register", "sr|ccr|usp|vbr"),
REGISTER_LIST("register list", "list"),
IMMEDIATE_DATA("immediate", "#<xxx>"),
ABSOLUTE_ADDRESS("absolute short/long", "(xxx).w|l")
@@ -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 {
@@ -197,6 +202,18 @@ object M68kIsa {
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX,
)
private val ALL_EXCEPT_AREG_AND_DREG = setOf(
AddressMode.ADDRESS_REGISTER_INDIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC,
AddressMode.ADDRESS_REGISTER_INDIRECT_PRE_DEC,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
AddressMode.ABSOLUTE_ADDRESS,
AddressMode.IMMEDIATE_DATA,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX,
)
private val INDIRECT_MODES = setOf(
AddressMode.ADDRESS_REGISTER_INDIRECT,
AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC,
@@ -210,38 +227,84 @@ 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, size = OP_SIZE_W, modInfo = RWM_MODIFY_OP1_OPSIZE, affectedCc = cc("*****")),
// not an official address mode, but supported by assembler (implicit #1)
AllowedAdrMode(DREG_ONLY, 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, size = OP_SIZE_W, modInfo = RWM_MODIFY_OP1_OPSIZE, affectedCc = cc("-**0*")),
// not an official address mode, but supported by assembler (implicit #1)
AllowedAdrMode(DREG_ONLY, 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, size = OP_SIZE_W, modInfo = RWM_MODIFY_OP1_OPSIZE, affectedCc = cc("***0*"), testedCc = cc("?----")),
// not an official address mode, but supported by assembler (implicit #1)
AllowedAdrMode(DREG_ONLY, 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), ALL_EXCEPT_AREG_AND_DREG, 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, ALL_EXCEPT_AREG_AND_DREG, 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), ALL_EXCEPT_AREG_AND_DREG, 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 +313,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 +351,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(
@@ -304,7 +372,9 @@ object M68kIsa {
AddressMode.ADDRESS_REGISTER_INDIRECT_POST_INC,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.ADDRESS_REGISTER_INDIRECT_WITH_INDEX,
AddressMode.ABSOLUTE_ADDRESS
AddressMode.ABSOLUTE_ADDRESS,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT,
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX
),
setOf(AddressMode.IMMEDIATE_DATA),
OP_SIZE_WL,
@@ -332,7 +402,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 +412,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 +465,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 +485,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 +553,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 +668,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 +730,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 +751,8 @@ object M68kIsa {
AddressMode.PROGRAM_COUNTER_INDIRECT_WITH_INDEX
), null, OP_UNSIZED
)
)
),
changesControlFlow = true
),
IsaData(
"jsr", "Jump to Subroutine",
@@ -637,27 +767,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 +830,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 +855,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 +865,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> {
@@ -783,5 +964,5 @@ object M68kIsa {
private fun isAddressModeMatching(am: AllowedAdrMode, op1: AddressMode?, op2: AddressMode?, specialReg: String?) =
((((op1 == null) && (am.op1 == null)) || am.op1?.contains(op1) ?: false)
&& (((op2 == null) && (am.op2 == null)) || am.op2?.contains(op2) ?: false)
&& ((specialReg == null) || (specialReg == am.specialReg)))
&& ((specialReg == null) || (specialReg.equals(am.specialReg, true))))
}
@@ -0,0 +1,47 @@
package de.platon42.intellij.plugins.m68k.documentation
import com.intellij.lang.documentation.AbstractDocumentationProvider
import com.intellij.lang.documentation.DocumentationMarkup
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.SmartList
import de.platon42.intellij.plugins.m68k.psi.M68kGlobalLabel
import de.platon42.intellij.plugins.m68k.psi.M68kStatement
class M68kGlobalLabelDocumentationProvider : AbstractDocumentationProvider() {
override fun getQuickNavigateInfo(element: PsiElement, originalElement: PsiElement?): String? {
return generateDoc(element, originalElement)
}
override fun generateDoc(element: PsiElement, originalElement: PsiElement?): String? {
return if (element is M68kGlobalLabel) {
// TODO find out how we can generate inner links for more symbol references inside the expression (DocumentationManagerUtil)
val statement = element.parent as M68kStatement
var preprocessorDirective = statement.preprocessorDirective
if ((preprocessorDirective == null) && (statement.asmInstruction == null) && (statement.macroCall == null)) {
val nextLineStatement = PsiTreeUtil.skipWhitespacesAndCommentsForward(PsiTreeUtil.skipWhitespacesAndCommentsForward(statement))
as? M68kStatement
preprocessorDirective = nextLineStatement?.preprocessorDirective
}
val content = if (preprocessorDirective != null)
DocumentationMarkup.CONTENT_START + StringUtil.escapeXmlEntities(preprocessorDirective.text) + DocumentationMarkup.CONTENT_END
else ""
val comments = SmartList<String>()
var prevToken: PsiElement? = statement
do {
prevToken = PsiTreeUtil.skipWhitespacesBackward(prevToken)
if (prevToken !is PsiComment) break
comments.add(prevToken.text)
} while (true)
val commentpart =
if (comments.isNotEmpty()) comments.asReversed().joinToString("<br>", DocumentationMarkup.GRAYED_START, DocumentationMarkup.GRAYED_END) else ""
commentpart +
DocumentationMarkup.DEFINITION_START + StringUtil.escapeXmlEntities(element.name!!) + DocumentationMarkup.DEFINITION_END +
content
} else null
}
}
@@ -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, allowedAdrMode.specialReg))
if (allowedAdrMode.op1 != null) cellsPerRow.add(collectAddressModes(allowedAdrMode.op2, allowedAdrMode.specialReg))
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))
@@ -82,11 +126,12 @@ class M68kInstructionDocumentationProvider : AbstractDocumentationProvider() {
return defBuilder
}
private fun collectAddressModes(addressModes: Set<AddressMode>?): HtmlChunk {
private fun collectAddressModes(addressModes: Set<AddressMode>?, specialReg: String?): HtmlChunk {
if (addressModes == null) return HtmlChunk.text("")
val modes = HtmlBuilder()
addressModes.sortedBy(AddressMode::ordinal)
.forEach { modes.append(HtmlChunk.text(it.syntax).wrapWith(HtmlChunk.div())) }
.map { if (it == AddressMode.SPECIAL_REGISTER_DIRECT) specialReg!! else it.syntax }
.forEach { modes.append(HtmlChunk.text(it).wrapWith(HtmlChunk.div())) }
return modes.toFragment()
}
@@ -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 ?: "-->"
@@ -79,18 +79,12 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
)
backtrace.addAll(analyseFlow(register, missingBits, true, initialStatement, linesLimit) {
PsiTreeUtil.getPrevSiblingOfType(
it,
M68kStatement::class.java
)
PsiTreeUtil.getPrevSiblingOfType(it, M68kStatement::class.java)
})
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,
M68kStatement::class.java
)
PsiTreeUtil.getNextSiblingOfType(it, M68kStatement::class.java)
})
val statementRows = HtmlBuilder()
@@ -108,12 +102,12 @@ 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 addAbbrevDots = false
var lines = 0
while (missingBits > 0) {
val globalLabel = PsiTreeUtil.findChildOfType(currStatement, M68kGlobalLabel::class.java)
@@ -127,17 +121,17 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
currStatement = direction.invoke(currStatement) ?: break
val currAsmInstruction = PsiTreeUtil.getChildOfType(currStatement, M68kAsmInstruction::class.java) ?: continue
if (checkIfInstructionUsesRegister(currAsmInstruction, register)) {
if (addAbrevDots) {
if (addAbbrevDots) {
++lines
statementLines.add(createAbbreviationDots())
}
if (++lines > linesLimit) {
if (!addAbrevDots) {
if (!addAbbrevDots) {
statementLines.add(createAbbreviationDots())
}
break
}
addAbrevDots = false
addAbbrevDots = false
val (_, currAdrMode) = findExactIsaDataAndAllowedAdrModeForInstruction(currAsmInstruction) ?: continue
val localLabelName = PsiTreeUtil.findChildOfType(currStatement, M68kLocalLabel::class.java)?.name ?: "        "
@@ -163,7 +157,7 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
.children(lineBuilder.wrapWith(DocumentationMarkup.SECTION_CONTENT_CELL))
)
} else {
addAbrevDots = true
addAbbrevDots = true
}
}
return statementLines
@@ -189,12 +183,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 +204,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
@@ -16,7 +16,7 @@ class M68kSymbolDefinitionDocumentationProvider : AbstractDocumentationProvider(
override fun generateDoc(element: PsiElement, originalElement: PsiElement?): String? {
return if (element is M68kSymbolDefinition) {
// TODO find out how we can generate inner links for more symbol references inside the expression (DocumentationManagerUtil)
val value = (element.getParent() as M68kAssignment).expr.text
val value = (element.parent as M68kAssignment).expr.text
DocumentationMarkup.DEFINITION_START + StringUtil.escapeXmlEntities(element.name!!) + DocumentationMarkup.DEFINITION_END +
DocumentationMarkup.CONTENT_START + StringUtil.escapeXmlEntities(value) + DocumentationMarkup.CONTENT_END
} else null
@@ -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()
}
}
@@ -0,0 +1,83 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.codeInspection.InspectionSuppressor
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.SuppressQuickFix
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import de.platon42.intellij.plugins.m68k.psi.M68kPsiElement
import de.platon42.intellij.plugins.m68k.psi.M68kStatement
class M68kInspectionSuppressor : InspectionSuppressor {
companion object {
const val MARKER = "suppress "
}
override fun isSuppressedFor(element: PsiElement, toolId: String): Boolean {
if (element !is M68kPsiElement) return false
val statement = PsiTreeUtil.getParentOfType(element, M68kStatement::class.java) ?: return false
val nextToken = PsiTreeUtil.skipWhitespacesForward(statement)
return isSuppressedWithComment(nextToken, toolId) || isSuppressedWithComment(PsiTreeUtil.skipWhitespacesBackward(statement), toolId)
}
override fun getSuppressActions(element: PsiElement?, toolId: String): Array<SuppressQuickFix> {
return arrayOf(LineSuppressQuickFix(toolId))
}
private fun isSuppressedWithComment(nextToken: PsiElement?, toolId: String): Boolean {
return if (nextToken is PsiComment) {
val comment = nextToken.text
comment.contains(MARKER) && comment.contains(toolId)
} else {
false
}
}
class LineSuppressQuickFix(private val toolId: String) : SuppressQuickFix {
override fun getFamilyName() = "Suppress for statement"
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val statement = PsiTreeUtil.getParentOfType(descriptor.startElement, M68kStatement::class.java) ?: return
val document = PsiDocumentManager.getInstance(project).getDocument(statement.containingFile) ?: return
val lineNumber = document.getLineNumber(statement.textOffset)
var usePrevLine = false
val nextToken = PsiTreeUtil.skipWhitespacesForward(statement)
if (nextToken is PsiComment) {
val comment = nextToken.text
if (comment.contains(MARKER)) {
if (!comment.contains(toolId)) {
document.insertString(document.getLineEndOffset(lineNumber), " $toolId")
}
return
} else {
usePrevLine = true
}
}
val prevToken = PsiTreeUtil.skipWhitespacesBackward(statement)
if (prevToken is PsiComment) {
val comment = prevToken.text
if (comment.contains(MARKER)) {
if (!comment.contains(toolId)) {
document.insertString(document.getLineEndOffset(document.getLineNumber(prevToken.textOffset)), " $toolId")
}
return
}
}
if (usePrevLine) {
document.insertString(document.getLineStartOffset(lineNumber), "; $MARKER$toolId\n")
} else {
document.insertString(document.getLineEndOffset(lineNumber), "\t; $MARKER$toolId")
}
}
override fun isAvailable(project: Project, context: PsiElement) = true
override fun isSuppressAll() = false
}
}
@@ -133,7 +133,6 @@ class M68kSyntaxInspection : AbstractBaseM68kLocalInspectionTool() {
)
}
val supportedOpSizes = findSupportedOpSizes(matchingModeIsaDataIgnoringSize, op1, op2, specialReg)
return arrayOf(
when (supportedOpSizes) {
@@ -0,0 +1,54 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.codeInspection.InspectionManager
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.psi.util.PsiTreeUtil
import de.platon42.intellij.plugins.m68k.asm.M68kIsa.findMatchingInstructions
import de.platon42.intellij.plugins.m68k.psi.*
import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil
import de.platon42.intellij.plugins.m68k.utils.M68kIsaUtil.findExactIsaDataAndAllowedAdrModeForInstruction
class M68kUnexpectedConditionalInstructionInspection : AbstractBaseM68kLocalInspectionTool() {
companion object {
private const val DISPLAY_NAME = "Unaffected condition codes before conditional instruction"
private const val UNAFFECTED_CONDITION_CODES_MSG_TEMPLATE = "Condition codes unaffected by instruction (%s - %s)"
}
override fun getDisplayName() = DISPLAY_NAME
override fun checkAsmInstruction(asmInstruction: M68kAsmInstruction, manager: InspectionManager, isOnTheFly: Boolean): Array<ProblemDescriptor>? {
val asmOp = asmInstruction.asmOp
if (asmInstruction.addressingModeList.isEmpty()) return emptyArray()
val isaDataCandidates = findMatchingInstructions(asmOp.mnemonic)
if (isaDataCandidates.isEmpty()) return emptyArray()
val (isaData, adrMode) = findExactIsaDataAndAllowedAdrModeForInstruction(asmInstruction) ?: return emptyArray()
if ((adrMode.affectedCc > 0) || (adrMode.testedCc > 0) || isaData.changesControlFlow) return emptyArray()
var currStatement = asmInstruction.parent as M68kStatement
while (true) {
currStatement = PsiTreeUtil.getNextSiblingOfType(currStatement, M68kStatement::class.java) ?: break
val globalLabel = PsiTreeUtil.findChildOfType(currStatement, M68kGlobalLabel::class.java)
if (globalLabel != null) break
if (PsiTreeUtil.findChildOfAnyType(currStatement, M68kMacroCall::class.java, M68kPreprocessorDirective::class.java) != null) break
val currAsmInstruction = PsiTreeUtil.getChildOfType(currStatement, M68kAsmInstruction::class.java) ?: continue
val (currIsaData, currAdrMode) = findExactIsaDataAndAllowedAdrModeForInstruction(currAsmInstruction) ?: break
val testedCc = M68kIsaUtil.getConcreteTestedCcFromMnemonic(currAsmInstruction.asmOp.mnemonic, currIsaData, currAdrMode)
if (testedCc == 0) break
return arrayOf(
manager.createProblemDescriptor(
asmInstruction,
asmInstruction,
UNAFFECTED_CONDITION_CODES_MSG_TEMPLATE.format(isaData.mnemonic, isaData.description),
ProblemHighlightType.WARNING,
isOnTheFly
)
)
}
return emptyArray()
}
}
@@ -0,0 +1,48 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.PsiPolyVariantReference
import de.platon42.intellij.plugins.m68k.psi.M68kMacroCall
import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference
import de.platon42.intellij.plugins.m68k.psi.M68kVisitor
import de.platon42.intellij.plugins.m68k.refs.M68kGlobalLabelSymbolReference
class M68kUnresolvedReferenceInspection : AbstractBaseM68kLocalInspectionTool() {
companion object {
private const val DISPLAY_NAME = "Unresolved label/symbol/macro reference"
}
override fun getDisplayName() = DISPLAY_NAME
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : M68kVisitor() {
override fun visitMacroCall(macroCall: M68kMacroCall) {
val reference = macroCall.reference as? PsiPolyVariantReference ?: return
val resolve = reference.multiResolve(false)
if (resolve.isEmpty()) {
holder.registerProblem(reference)
}
}
override fun visitSymbolReference(symbolReference: M68kSymbolReference) {
val references = symbolReference.references ?: return
if (references.isEmpty()) return
val resolve = references.mapNotNull { it as? PsiPolyVariantReference }
.firstNotNullOfOrNull { it.multiResolve(false).ifEmpty { null } }
if (resolve == null) {
// TODO currently, because macro invocations are not evaluated, mark missing symbols only as weak warning
val makeWeak = references.any { it is M68kGlobalLabelSymbolReference }
if (makeWeak) {
holder.registerProblem(references.first(), ProblemHighlightType.WEAK_WARNING)
} else {
holder.registerProblem(references.first())
}
}
}
}
}
}
@@ -19,11 +19,17 @@ object LexerUtil {
&& text.dropLast(1).endsWith('.')
&& mnemonics.contains(text.dropLast(2).toString().lowercase())
@JvmStatic
fun isEndDirective(text: CharSequence) = text.contentEquals("end", ignoreCase = true)
@JvmStatic
fun isDataDirective(text: CharSequence) = AssemblerDirectives.dataDirectives.contains(text.toString().lowercase())
@JvmStatic
fun isOtherDirective(text: CharSequence) = AssemblerDirectives.otherDirective.contains(text.toString().lowercase())
fun isPlainDirective(text: CharSequence) = AssemblerDirectives.plainDirectives.contains(text.toString().lowercase())
@JvmStatic
fun isOtherDirective(text: CharSequence) = AssemblerDirectives.otherDirectives.contains(text.toString().lowercase())
@JvmStatic
fun pushbackAssignment(text: CharSequence): Int {
@@ -52,7 +58,7 @@ object LexerUtil {
@JvmStatic
fun handleMacroMode(lexer: _M68kLexer): IElementType {
if (lexer.lexerPrefs.macroParametersUnparsed) {
lexer.yybegin(_M68kLexer.MACROCALL)
lexer.yybegin(_M68kLexer.PLAINPARAMS)
} else {
lexer.yybegin(_M68kLexer.ASMOPS)
}
@@ -69,4 +75,13 @@ object LexerUtil {
lexer.yybegin(_M68kLexer.MACROLINE)
return TokenType.WHITE_SPACE
}
fun unquoteString(string: String) = string.run {
when {
startsWith('"') -> removeSurrounding("\"")
startsWith('\'') -> removeSurrounding("'")
startsWith('<') -> removeSurrounding("<", ">")
else -> this
}
}
}
@@ -72,7 +72,7 @@ HASH_COMMENT=([#;*].*+)
SKIP_TO_EOL=[^\r\n]+
PLAIN_MACRO_LINE=[^;\r\n]+
%state NOSOL,INSTRPART,ASMINSTR,ASMOPS,ASMOPS_OP,ASSIGNMENT,EXPR,EXPR_OP,MACROCALL,WAITEOL,MACRODEF,MACROLINE,MACROTERMINATION,MACROWAITEOL
%state NOSOL,INSTRPART,ASMINSTR,ASMOPS,ASMOPS_OP,ASSIGNMENT,EXPR,EXPR_OP,PLAINPARAMS,WAITEOL,MACRODEF,MACROLINE,MACROTERMINATION,MACROWAITEOL
%%
<YYINITIAL, NOSOL>
@@ -81,7 +81,7 @@ PLAIN_MACRO_LINE=[^;\r\n]+
{HASH_COMMENT} { yybegin(YYINITIAL); return COMMENT; }
}
<INSTRPART,ASMINSTR,MACROCALL,ASSIGNMENT,EXPR,EXPR_OP,ASMOPS,ASMOPS_OP>
<INSTRPART,ASMINSTR,PLAINPARAMS,ASSIGNMENT,EXPR,EXPR_OP,ASMOPS,ASMOPS_OP>
{
{EOL} { yybegin(YYINITIAL); return EOL; }
{COMMENT} { yybegin(WAITEOL); return COMMENT; }
@@ -92,7 +92,7 @@ PLAIN_MACRO_LINE=[^;\r\n]+
{WHITE_SPACE} { return WHITE_SPACE; }
}
<MACROCALL, EXPR, EXPR_OP, ASMOPS, ASMOPS_OP> {
<PLAINPARAMS, EXPR, EXPR_OP, ASMOPS, ASMOPS_OP> {
{WHITE_SPACE} { return handleEolCommentWhitespace(this); }
}
@@ -102,6 +102,7 @@ PLAIN_MACRO_LINE=[^;\r\n]+
{MACRO_DEF_LEFT} { yybegin(MACRODEF); yypushback(pushbackAfterFirstToken(yytext())); return MACRO_NAME; }
{LOCAL_LABEL} { yybegin(INSTRPART); eatOneWhitespace = false; yypushback(pushbackLabelColons(yytext())); return LOCAL_LABEL_DEF; }
{GLOBAL_LABEL} { yybegin(INSTRPART); eatOneWhitespace = false; yypushback(pushbackLabelColons(yytext())); return GLOBAL_LABEL_DEF; }
"." { yybegin(INSTRPART); eatOneWhitespace = false; return LOCAL_LABEL_DEF; }
}
<NOSOL> {
@@ -116,7 +117,9 @@ PLAIN_MACRO_LINE=[^;\r\n]+
{DIRECTIVE_KEYWORD} {
if(isAsmMnemonicWithSize(yytext())) { yybegin(ASMINSTR); yypushback(2); return MNEMONIC; }
if(isAsmMnemonic(yytext())) { yybegin(ASMINSTR); return MNEMONIC; }
//if(isEndDirective(yytext())) { yybegin(YYINITIAL); zzAtEOF = true; return null; }
if(isDataDirective(yytext())) { startExpr(EXPR, EXPR_OP); return DATA_DIRECTIVE; }
if(isPlainDirective(yytext())) { yybegin(PLAINPARAMS); return OTHER_DIRECTIVE; }
if(isOtherDirective(yytext())) { startExpr(EXPR, EXPR_OP); return OTHER_DIRECTIVE; }
return handleMacroMode(this);
}
@@ -136,7 +139,7 @@ PLAIN_MACRO_LINE=[^;\r\n]+
{OPSIZE_L} { return OPSIZE_L; }
}
<MACROCALL> {
<PLAINPARAMS> {
"," { return SEPARATOR; }
{PLAINPARAM} { return STRINGLIT; }
@@ -121,11 +121,10 @@ M68kFile ::= line*
private line ::= !<<eof>> (MacroDefinition | statement) (<<eof>>|EOL)
statement ::= (Assignment
| PreprocessorDirective
| LabelInsts)
{pin=1 recoverWhile=statement_recover};
{pin = 1 recoverWhile = statement_recover}
private statement_recover ::= !(EOL)
private statement_recover ::= !(EOL) { consumeTokenMethod = "consumeTokenFast" }
SymbolDefinition ::= SYMBOLDEF {
implements = "de.platon42.intellij.plugins.m68k.psi.M68kNamedElement"
@@ -140,8 +139,8 @@ Assignment ::= SymbolDefinition COLON? (OP_ASSIGN|EQU) expr
private LabelInsts ::= LabelWithInstruction | LabelOnly | InstructionOnly
private LabelOnly ::= Label
private LabelWithInstruction ::= Label Instruction
private InstructionOnly ::= Instruction
private LabelWithInstruction ::= Label (Instruction|PreprocessorDirective)
private InstructionOnly ::= (Instruction|PreprocessorDirective)
LocalLabel ::= LOCAL_LABEL_DEF COLON? {
name = "local label"
@@ -173,8 +172,11 @@ AsmOp ::= MNEMONIC OperandSize? {
methods = [getMnemonic getOpSize]
}
PreprocessorDirective ::= Label? (DATA_DIRECTIVE | OTHER_DIRECTIVE)
PreprocessorOperands?
PreprocessorKeyword ::= (DATA_DIRECTIVE | OTHER_DIRECTIVE)
PreprocessorDirective ::= PreprocessorKeyword PreprocessorOperands? {
mixin = "de.platon42.intellij.plugins.m68k.psi.M68kPreprocessorDirectiveMixin"
}
MacroPlainLine ::= MACRO_LINE
MacroNameDefinition ::= MACRO_NAME
@@ -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(
@@ -62,11 +62,4 @@ object M68kAddressModeUtil {
else -> throw IllegalArgumentException("Unknown addressing mode $addressingMode")
}
}
fun mergeReadWriteModifyRegisters(regset: Set<Pair<Register, Int>>): Set<Pair<Register, Int>> {
if (regset.size <= 1) return regset
return regset.groupBy({ it.first }) { it.second }
.map { it.key to if (it.value.size == 1) it.value.single() else it.value.reduce(Int::or) }
.toSet()
}
}
@@ -37,6 +37,11 @@ object M68kElementFactory {
return PsiTreeUtil.findChildOfType(file, M68kMacroCall::class.java)!!
}
fun createIncludeStatement(project: Project, path: String): M68kPreprocessorDirective {
val file = createFile(project, " include \"$path\"\n")
return PsiTreeUtil.findChildOfType(file, M68kPreprocessorDirective::class.java)!!
}
fun createFile(project: Project, content: String): M68kFile {
return PsiFileFactory.getInstance(project).createFileFromText("dummy.m68k", INSTANCE, content) as M68kFile
}
@@ -2,21 +2,13 @@ package de.platon42.intellij.plugins.m68k.psi
import com.intellij.extapi.psi.ASTWrapperPsiElement
import com.intellij.lang.ASTNode
import de.platon42.intellij.plugins.m68k.lexer.LexerUtil
abstract class M68kLiteralExprMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kLiteralExpr {
override fun getValue(): Any? {
val childNode = firstChild.node
when (childNode.elementType) {
M68kTypes.STRINGLIT -> {
return text.run {
when {
startsWith('"') -> removeSurrounding("\"")
startsWith('\'') -> removeSurrounding("'")
startsWith('<') -> removeSurrounding(">")
else -> this
}
}
}
M68kTypes.STRINGLIT -> LexerUtil.unquoteString(childNode.text)
M68kTypes.DECIMAL -> {
try {
return childNode.text.toInt()
@@ -46,6 +38,6 @@ abstract class M68kLiteralExprMixin(node: ASTNode) : ASTWrapperPsiElement(node),
}
}
}
return text
return childNode.text
}
}
@@ -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)
}
@@ -7,6 +7,10 @@ import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry
abstract class M68kMacroCallMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kMacroCall {
override fun getReference(): PsiReference? {
return references.firstOrNull()
}
override fun getReferences(): Array<PsiReference> {
return ReferenceProvidersRegistry.getReferencesFromProviders(this)
}
@@ -0,0 +1,17 @@
package de.platon42.intellij.plugins.m68k.psi
import com.intellij.extapi.psi.ASTWrapperPsiElement
import com.intellij.lang.ASTNode
import com.intellij.psi.PsiReference
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry
abstract class M68kPreprocessorDirectiveMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kPreprocessorDirective {
override fun getReference(): PsiReference? {
return references.firstOrNull()
}
override fun getReferences(): Array<PsiReference> {
return ReferenceProvidersRegistry.getReferencesFromProviders(this)
}
}
@@ -29,7 +29,7 @@ object M68kPsiImplUtil {
// Local Label
@JvmStatic
fun getName(element: M68kLocalLabel): String? = element.firstChild.text
fun getName(element: M68kLocalLabel): String? = element.firstChild?.text
@JvmStatic
fun setName(element: M68kLocalLabel, name: String): PsiElement {
@@ -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)
@@ -7,6 +7,10 @@ import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry
abstract class M68kSymbolReferenceMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kSymbolReference {
override fun getReference(): PsiReference? {
return references.firstOrNull()
}
override fun getReferences(): Array<PsiReference> {
return ReferenceProvidersRegistry.getReferencesFromProviders(this)
}
@@ -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()
// }
@@ -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)
{
@@ -0,0 +1,46 @@
package de.platon42.intellij.plugins.m68k.refs
import com.intellij.psi.*
import com.intellij.psi.impl.source.resolve.ResolveCache
import com.intellij.psi.search.FileTypeIndex
import com.intellij.psi.search.GlobalSearchScope
import de.platon42.intellij.plugins.m68k.M68kFileType
import de.platon42.intellij.plugins.m68k.lexer.LexerUtil
import de.platon42.intellij.plugins.m68k.psi.M68kPreprocessorDirective
class M68kIncludeFileReference(element: M68kPreprocessorDirective) : PsiPolyVariantReferenceBase<M68kPreprocessorDirective>(element, true) {
companion object {
val INSTANCE = Resolver()
}
class Resolver : ResolveCache.PolyVariantResolver<M68kIncludeFileReference> {
override fun resolve(ref: M68kIncludeFileReference, incompleteCode: Boolean): Array<ResolveResult> {
val project = ref.element.project
val allFiles = FileTypeIndex.getFiles(M68kFileType.INSTANCE, GlobalSearchScope.allScope(project))
val pathName = LexerUtil.unquoteString(ref.element.exprList.first().text).replace('\\', '/')
val fileMatched = ((allFiles.firstOrNull { it.path.equals(pathName, ignoreCase = true) }
?: allFiles.firstOrNull { it.path.endsWith(pathName.removePrefix("../"), ignoreCase = true) })
?: allFiles.firstOrNull { it.path.endsWith(pathName.substringAfterLast('/'), ignoreCase = true) }) ?: return emptyArray()
return PsiElementResolveResult.createResults(PsiManager.getInstance(project).findFile(fileMatched))
}
}
override fun multiResolve(incompleteCode: Boolean): Array<ResolveResult> =
ResolveCache.getInstance(element.project)
.resolveWithCaching(this, INSTANCE, false, incompleteCode)
override fun resolve(): PsiElement? = multiResolve(false).singleOrNull()?.element
override fun getVariants(): Array<Any> = emptyArray()
// override fun getRangeInElement(): TextRange {
// val unquoted = LexerUtil.unquoteString(element.text)
//
// val path = element.exprList.first()
// return TextRange.from(
// path.textRange.startOffset + path.text.indexOf(unquoted), unquoted.length
// )
// }
}
@@ -0,0 +1,68 @@
package de.platon42.intellij.plugins.m68k.refs
import com.intellij.codeInsight.completion.*
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.patterns.PlatformPatterns
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.ProcessingContext
import de.platon42.intellij.plugins.m68k.psi.*
class M68kLocalLabelDefCompletionContributor : CompletionContributor() {
init {
extend(
CompletionType.BASIC,
PlatformPatterns.or(PlatformPatterns.psiElement(M68kTypes.LOCAL_LABEL_DEF), PlatformPatterns.psiElement(M68kTypes.GLOBAL_LABEL_DEF)),
object : CompletionProvider<CompletionParameters>() {
override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, resultSet: CompletionResultSet) {
var topLevelElement = parameters.originalFile.findElementAt(parameters.offset)
while (topLevelElement?.parent !is M68kFile) {
topLevelElement = topLevelElement?.parent ?: return
}
// TODO find out if we can cache this somehow
val affectedStatements = ArrayList<M68kStatement>()
val definedLocalLabels = HashSet<String>()
val referencedLocalLabels = HashSet<String>()
findUndefinedLocalLabels(topLevelElement, affectedStatements, definedLocalLabels, referencedLocalLabels) {
PsiTreeUtil.getNextSiblingOfType(it, M68kStatement::class.java)
}
findUndefinedLocalLabels(topLevelElement, affectedStatements, definedLocalLabels, referencedLocalLabels) {
PsiTreeUtil.getPrevSiblingOfType(it, M68kStatement::class.java)
}
referencedLocalLabels.removeAll(definedLocalLabels)
resultSet.addAllElements(
if (parameters.originalPosition?.text == ".") {
referencedLocalLabels.map { LookupElementBuilder.create(it.removePrefix(".")) }
} else {
referencedLocalLabels.map(LookupElementBuilder::create)
}
)
}
private fun findUndefinedLocalLabels(
topLevelElement: PsiElement,
affectedStatements: MutableList<M68kStatement>,
definedLocalLabels: MutableSet<String>,
referencedLocalLabels: MutableSet<String>,
direction: (topLevelElement: PsiElement) -> M68kStatement?
) {
var currStatement = topLevelElement
while (true) {
currStatement = direction.invoke(currStatement) ?: break
val globalLabel = PsiTreeUtil.findChildOfType(currStatement, M68kGlobalLabel::class.java)
if (globalLabel != null) break
affectedStatements.add(currStatement)
val localLabel = PsiTreeUtil.findChildOfType(currStatement, M68kLocalLabel::class.java)
if (localLabel != null) definedLocalLabels.add(localLabel.name!!)
val symbolReferences = PsiTreeUtil.findChildrenOfAnyType(currStatement, M68kSymbolReference::class.java)
if (symbolReferences.isNotEmpty()) {
referencedLocalLabels.addAll(
symbolReferences.filter(M68kSymbolReference::isLocalLabelRef).map(M68kSymbolReference::getSymbolName)
)
}
}
}
})
}
}
@@ -1,5 +1,6 @@
package de.platon42.intellij.plugins.m68k.refs
import com.intellij.codeInsight.daemon.EmptyResolveMessageProvider
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
@@ -14,14 +15,18 @@ import de.platon42.intellij.plugins.m68k.psi.M68kLocalLabel
import de.platon42.intellij.plugins.m68k.psi.M68kStatement
import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference
class M68kLocalLabelReference(element: M68kSymbolReference) : PsiPolyVariantReferenceBase<M68kSymbolReference>(element, TextRange(0, element.textLength)) {
class M68kLocalLabelReference(element: M68kSymbolReference) :
PsiPolyVariantReferenceBase<M68kSymbolReference>(element, TextRange(0, element.textLength)),
EmptyResolveMessageProvider {
companion object {
val INSTANCE = Resolver()
private const val UNRESOLVED_MESSAGE_PATTERN = "Cannot resolve local label ''{0}''"
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) {
@@ -42,6 +47,8 @@ class M68kLocalLabelReference(element: M68kSymbolReference) : PsiPolyVariantRefe
}
}
override fun getUnresolvedMessagePattern() = UNRESOLVED_MESSAGE_PATTERN
class Resolver : ResolveCache.PolyVariantResolver<M68kLocalLabelReference> {
override fun resolve(ref: M68kLocalLabelReference, incompleteCode: Boolean): Array<ResolveResult> {
val refName = ref.element.symbolName
@@ -1,5 +1,6 @@
package de.platon42.intellij.plugins.m68k.refs
import com.intellij.codeInsight.daemon.EmptyResolveMessageProvider
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementResolveResult
@@ -14,18 +15,22 @@ import de.platon42.intellij.plugins.m68k.psi.M68kMacroDefinition
import de.platon42.intellij.plugins.m68k.stubs.M68kMacroDefinitionStubIndex
class M68kMacroReference(element: M68kMacroCall) :
PsiPolyVariantReferenceBase<M68kMacroCall>(element, TextRange(0, element.macroName.length)) {
PsiPolyVariantReferenceBase<M68kMacroCall>(element, TextRange(0, element.macroName.length)), EmptyResolveMessageProvider {
companion object {
val INSTANCE = Resolver()
private const val UNRESOLVED_MESSAGE_PATTERN = "Cannot resolve macro ''{0}''"
}
override fun getUnresolvedMessagePattern() = UNRESOLVED_MESSAGE_PATTERN
class Resolver : ResolveCache.PolyVariantResolver<M68kMacroReference> {
override fun resolve(ref: M68kMacroReference, incompleteCode: Boolean): Array<ResolveResult> {
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)
{
@@ -0,0 +1,34 @@
package de.platon42.intellij.plugins.m68k.refs
import com.intellij.history.core.Paths
import com.intellij.openapi.util.TextRange
import com.intellij.psi.AbstractElementManipulator
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.IncorrectOperationException
import de.platon42.intellij.plugins.m68k.lexer.LexerUtil
import de.platon42.intellij.plugins.m68k.psi.M68kElementFactory
import de.platon42.intellij.plugins.m68k.psi.M68kLiteralExpr
import de.platon42.intellij.plugins.m68k.psi.M68kPreprocessorDirective
class M68kPreprocessorDirectiveElementManipulator : AbstractElementManipulator<M68kPreprocessorDirective>() {
@Throws(IncorrectOperationException::class)
override fun handleContentChange(element: M68kPreprocessorDirective, range: TextRange, newContent: String): M68kPreprocessorDirective {
val oldPath = LexerUtil.unquoteString(element.exprList.first().text)
.replace('\\', '/')
.substringBeforeLast('/', missingDelimiterValue = "")
val newIncludeStatement = M68kElementFactory.createIncludeStatement(element.project, Paths.appended(oldPath, newContent))
PsiTreeUtil.findChildOfType(element, M68kLiteralExpr::class.java)!!.replace(newIncludeStatement.exprList.first())
return element
}
override fun getRangeInElement(element: M68kPreprocessorDirective): TextRange {
val pathExpr = element.exprList.first()
val unquotedPath = LexerUtil.unquoteString(pathExpr.text)
return if (unquotedPath.length < pathExpr.textLength) {
TextRange.from(pathExpr.startOffsetInParent + 1, unquotedPath.length)
} else {
pathExpr.textRangeInParent
}
}
}
@@ -3,7 +3,9 @@ package de.platon42.intellij.plugins.m68k.refs
import com.intellij.patterns.PlatformPatterns
import com.intellij.psi.*
import com.intellij.util.ProcessingContext
import de.platon42.intellij.plugins.m68k.psi.M68kLiteralExpr
import de.platon42.intellij.plugins.m68k.psi.M68kMacroCall
import de.platon42.intellij.plugins.m68k.psi.M68kPreprocessorDirective
import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference
class M68kReferenceContributor : PsiReferenceContributor() {
@@ -12,12 +14,14 @@ class M68kReferenceContributor : PsiReferenceContributor() {
val localLabelReferenceProvider = LocalLabelReferenceProvider()
val globalLabelReferenceProvider = GlobalLabelSymbolReferenceProvider()
val macroReferenceProvider = MacroReferenceProvider()
val includeFileReferenceProvider = IncludeFileReferenceProvider()
}
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kSymbolReference::class.java), localLabelReferenceProvider)
registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kSymbolReference::class.java), globalLabelReferenceProvider)
registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kMacroCall::class.java), macroReferenceProvider)
registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kPreprocessorDirective::class.java), includeFileReferenceProvider)
}
class LocalLabelReferenceProvider : PsiReferenceProvider() {
@@ -40,4 +44,16 @@ class M68kReferenceContributor : PsiReferenceContributor() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> =
arrayOf(M68kMacroReference(element as M68kMacroCall))
}
class IncludeFileReferenceProvider : PsiReferenceProvider() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
val directive = element as M68kPreprocessorDirective
if (directive.exprList.firstOrNull() !is M68kLiteralExpr) return emptyArray()
return if (directive.preprocessorKeyword.text.equals("include", ignoreCase = true)) {
arrayOf(M68kIncludeFileReference(directive))
} else {
emptyArray()
}
}
}
}
@@ -1,5 +1,6 @@
package de.platon42.intellij.plugins.m68k.scanner
import com.intellij.lang.HelpID
import com.intellij.lang.cacheBuilder.DefaultWordsScanner
import com.intellij.lang.cacheBuilder.WordsScanner
import com.intellij.lang.findUsages.FindUsagesProvider
@@ -22,14 +23,19 @@ class M68kFindUsagesProvider : FindUsagesProvider {
TokenSet.EMPTY
)
override fun canFindUsagesFor(psiElement: PsiElement): Boolean = psiElement is M68kNamedElement
override fun canFindUsagesFor(psiElement: PsiElement): Boolean =
when (psiElement) {
is M68kNamedElement, is M68kSymbolReference, is M68kMacroCall, is M68kFile, is M68kRegister -> true
else -> false
}
override fun getHelpId(psiElement: PsiElement): @NonNls String? = null
override fun getHelpId(psiElement: PsiElement): @NonNls String = HelpID.FIND_OTHER_USAGES
override fun getType(element: PsiElement): @Nls String {
return when (element) {
is M68kGlobalLabel -> "global label"
is M68kLocalLabel -> "local label"
is M68kPreprocessorDirective -> "preprocessor directive"
is M68kSymbolDefinition -> "symbol definition"
is M68kSymbolReference -> "symbol reference"
is M68kMacroDefinition -> "macro definition"
@@ -44,6 +50,7 @@ class M68kFindUsagesProvider : FindUsagesProvider {
return when (element) {
is M68kGlobalLabel -> element.name!!
is M68kLocalLabel -> element.name!!
is M68kPreprocessorDirective -> element.text
is M68kSymbolDefinition -> element.parent.text
is M68kSymbolReference -> element.symbolName
is M68kMacroDefinition -> element.macroNameDefinition.text
@@ -0,0 +1,59 @@
package de.platon42.intellij.plugins.m68k.scanner
import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.fileTypes.FileTypeRegistry
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.impl.include.FileIncludeInfo
import com.intellij.psi.impl.include.FileIncludeProvider
import com.intellij.psi.impl.source.tree.LightTreeUtil
import com.intellij.util.Consumer
import com.intellij.util.PathUtilRt
import com.intellij.util.indexing.FileContent
import com.intellij.util.indexing.PsiDependentFileContent
import de.platon42.intellij.plugins.m68k.M68kFileType.Companion.INSTANCE
import de.platon42.intellij.plugins.m68k.lexer.LexerUtil
import de.platon42.intellij.plugins.m68k.psi.M68kTypes
class M68kIncludeFileProvider : FileIncludeProvider() {
override fun getId(): String {
return "mc68000_include"
}
override fun acceptFile(file: VirtualFile): Boolean {
return FileTypeRegistry.getInstance().isFileOfType(file, INSTANCE)
}
override fun registerFileTypesUsedForIndexing(fileTypeSink: Consumer<in FileType?>) {
fileTypeSink.consume(INSTANCE)
}
override fun getIncludeInfos(content: FileContent): Array<FileIncludeInfo> {
if (content.contentAsText.indexOfAny(listOf("include", "incbin"), ignoreCase = true) < 0) return emptyArray()
val tree = (content as PsiDependentFileContent).lighterAST
val statements = LightTreeUtil.getChildrenOfType(tree, tree.root, M68kTypes.STATEMENT).asSequence()
.mapNotNull { LightTreeUtil.firstChildOfType(tree, it, M68kTypes.PREPROCESSOR_DIRECTIVE) }
.mapNotNull {
val keywordNode = LightTreeUtil.firstChildOfType(tree, it, M68kTypes.PREPROCESSOR_KEYWORD) ?: return@mapNotNull null
val keyword = LightTreeUtil.toFilteredString(tree, keywordNode, null)
if (keyword.equals("include", true) || keyword.equals("incbin", true)) {
keyword to it
} else {
null
}
}
.mapNotNull {
val pathNode = LightTreeUtil.firstChildOfType(tree, it.second, M68kTypes.LITERAL_EXPR) ?: return@mapNotNull null
val path = LexerUtil.unquoteString(LightTreeUtil.toFilteredString(tree, pathNode, null)).replace('\\', '/')
if (it.first.equals("include", true)) {
FileIncludeInfo(path)
} else {
FileIncludeInfo(PathUtilRt.getFileName(path), path, 0, true)
}
}
.toList()
return statements.toTypedArray()
}
}
@@ -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) {
+18 -2
View File
@@ -16,7 +16,7 @@
<extensions defaultExtensionNs="com.intellij">
<fileType name="MC68000 Assembler" implementationClass="de.platon42.intellij.plugins.m68k.M68kFileType"
fieldName="INSTANCE" language="MC68000" extensions="asm;ASM;s;S;i"/>
fieldName="INSTANCE" language="MC68000" extensions="asm;s;i"/>
<lang.parserDefinition language="MC68000"
implementationClass="de.platon42.intellij.plugins.m68k.parser.M68kParserDefinition"/>
<lang.syntaxHighlighterFactory language="MC68000"
@@ -24,17 +24,23 @@
<colorSettingsPage implementation="de.platon42.intellij.plugins.m68k.syntax.M68kColorSettingsPage"/>
<completion.contributor language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.asm.M68kMnemonicCompletionContributor"/>
<completion.contributor language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kGlobalLabelSymbolCompletionContributor"/>
<completion.contributor language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kLocalLabelDefCompletionContributor"/>
<completion.contributor language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kMacroCallCompletionContributor"/>
<lang.braceMatcher language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.syntax.M68kPairedBraceMatcher"/>
<lang.quoteHandler language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.M68kStringQuoteHandler"/>
<lang.findUsagesProvider language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.scanner.M68kFindUsagesProvider"/>
<lang.psiStructureViewFactory language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.structureview.M68kStructureViewFactory"/>
<lang.inspectionSuppressor language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kInspectionSuppressor"/>
<lang.elementManipulator forClass="de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference"
implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kSymbolReferenceElementManipulator"/>
<lang.elementManipulator forClass="de.platon42.intellij.plugins.m68k.psi.M68kMacroCall"
implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kMacroCallElementManipulator"/>
<lang.elementManipulator forClass="de.platon42.intellij.plugins.m68k.psi.M68kPreprocessorDirective"
implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kPreprocessorDirectiveElementManipulator"/>
<lang.documentationProvider language="MC68000"
implementationClass="de.platon42.intellij.plugins.m68k.documentation.M68kSymbolDefinitionDocumentationProvider"/>
<lang.documentationProvider language="MC68000"
implementationClass="de.platon42.intellij.plugins.m68k.documentation.M68kGlobalLabelDocumentationProvider"/>
<lang.documentationProvider language="MC68000"
implementationClass="de.platon42.intellij.plugins.m68k.documentation.M68kRegisterFlowDocumentationProvider"/>
<lang.documentationProvider language="MC68000"
@@ -43,13 +49,23 @@
<stubIndex implementation="de.platon42.intellij.plugins.m68k.stubs.M68kGlobalLabelStubIndex"/>
<stubIndex implementation="de.platon42.intellij.plugins.m68k.stubs.M68kSymbolDefinitionStubIndex"/>
<stubIndex implementation="de.platon42.intellij.plugins.m68k.stubs.M68kMacroDefinitionStubIndex"/>
<psi.referenceContributor implementation="de.platon42.intellij.plugins.m68k.refs.M68kReferenceContributor"/>
<psi.referenceContributor language="MC68000" implementation="de.platon42.intellij.plugins.m68k.refs.M68kReferenceContributor"/>
<gotoSymbolContributor implementation="de.platon42.intellij.plugins.m68k.refs.M68kChooseByNameContributor"/>
<renameInputValidator implementation="de.platon42.intellij.plugins.m68k.psi.M68kRenameInputValidator"/>
<include.provider implementation="de.platon42.intellij.plugins.m68k.scanner.M68kIncludeFileProvider"/>
<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"/>
<localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kUnresolvedReferenceInspection"
displayName="Unresolved label/symbol/macro reference" 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>
@@ -0,0 +1,8 @@
<html>
<body>
Checks if references to symbols, macros, global and local labels are resolvable.
<!-- tooltip end -->
<p>Currently, issues a weak warning for global labels or symbols as those
can be defined via macros, which are not evaluated right now</p>
</body>
</html>
@@ -0,0 +1,43 @@
package de.platon42.intellij.plugins.m68k.documentation
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.LightCodeInsightExtension
import de.platon42.intellij.jupiter.MyFixture
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@ExtendWith(LightCodeInsightExtension::class)
internal class M68kGlobalLabelDocumentationProviderTest : AbstractDocumentationProviderTest() {
@Test
internal fun check_documentation_for_a_label_definition(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByText(
"documentme.asm", """
rts
; inputs: d0 = number
; output: d0 = square
squareme: include "fancysquarecode.asm"
rts
bsr square<caret>me
"""
)
assertThat(generateDocumentation(myFixture))
.isEqualTo("<span class='grayed'>; inputs: d0 = number<br>; output: d0 = square</span><div class='definition'><pre>squareme</pre></div><div class='content'>include &quot;fancysquarecode.asm&quot;</div>")
}
@Test
internal fun check_documentation_for_a_label_definition_next_line(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByText(
"documentme.asm", """
; output: d0 = square
square<caret>me:
; oh man
include "fancysquarecode.asm"
"""
)
assertThat(generateDocumentation(myFixture))
.isEqualTo("<span class='grayed'>; output: d0 = square</span><div class='definition'><pre>squareme</pre></div><div class='content'>include &quot;fancysquarecode.asm&quot;</div>")
}
}
@@ -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>#&lt;xxx&gt;</div></td><td valign="top"><div>Dn</div></td></tr></table></div>
<div class="definition">
<pre><b>Move Quick</b></pre>
</div>
<div class="content">
<table class="sections">
<tr>
<td class="section" valign="top">Mnemonic / CCs</td>
<td class="section" valign="top">Operand 1</td>
<td class="section" valign="top">Operand 2</td>
</tr>
<tr>
<td valign="top">moveq.l
<hr/>
Condition Codes: <br/>
<table class="sections">
<tr>
<td class="section" valign="top">X</td>
<td class="section" valign="top">N</td>
<td class="section" valign="top">Z</td>
<td class="section" valign="top">V</td>
<td class="section" valign="top">C</td>
</tr>
<tr>
<td valign="top">-</td>
<td valign="top">*</td>
<td valign="top">*</td>
<td valign="top">0</td>
<td valign="top">0</td>
</tr>
</table>
X - Not affected<br/>N - From result (usually if negative)<br/>Z - From result (usually if zero)<br/>V - Always cleared<br/>C - Always cleared
</td>
<td valign="top">
<div>#&lt;xxx&gt;</div>
</td>
<td valign="top">
<div>Dn</div>
</td>
</tr>
</table>
</div>
"""
)
}
@@ -39,15 +79,13 @@ internal class M68kInstructionDocumentationProviderTest : AbstractDocumentationP
<div class="content">
<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>
@@ -70,6 +108,16 @@ internal class M68kInstructionDocumentationProviderTest : AbstractDocumentationP
assertThat(generateDocumentation(myFixture)).contains("Reset External Devices", "(privileged)")
}
@Test
internal fun check_documentation_for_instruction_with_special_register(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByText(
"documentme.asm", """
<caret>move.l usp,a0
"""
)
assertThat(generateDocumentation(myFixture)).contains("<div>usp</div>")
}
@Test
internal fun check_documentation_if_there_is_no_concrete_match(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByText(
@@ -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">&nbsp;</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">--&gt;</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">&nbsp;</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">&nbsp;</td>
<td valign="top">
<div class="grayed">[...]</div>
</td>
<td valign="top">&nbsp;</td>
</tr>
<tr>
<td valign="top">--&gt;</td>
<td valign="top"><b><code>move.w d0,<font color="#ffc800">d1</font></code></b></td>
<td valign="top"> ; &lt;--</td>
</tr>
<tr>
<td valign="top">&nbsp;</td>
<td valign="top">
<div class="grayed">[...]</div>
</td>
<td valign="top">&nbsp;</td>
</tr>
<tr>
<td valign="top">        </td>
<td valign="top"><code>move.w <font color="#ffc800">d1</font>,d2</code></td>
<td valign="top"> ; uses d1.w</td>
</tr>
<tr>
<td valign="top">&nbsp;</td>
<td valign="top">
<div class="grayed">[...]</div>
</td>
<td valign="top">&nbsp;</td>
</tr>
<tr>
<td valign="top">        </td>
<td valign="top"><code>move.l (sp)+,<font color="#ffc800">d1</font></code></td>
<td valign="top"> ; <font color="#00ff00">sets d1.l</font></td>
</tr>
</table>
"""
)
}
}
@@ -1,5 +1,6 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.LightCodeInsightExtension
import de.platon42.intellij.jupiter.TestDataPath
@@ -11,8 +12,21 @@ 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"))
}
protected fun executeQuickFixes(myFixture: CodeInsightTestFixture, regex: Regex, expectedFixes: Int) {
val quickfixes = getQuickFixes(myFixture, regex, expectedFixes)
assertThat(quickfixes.groupBy { it.familyName }).hasSize(1)
quickfixes.forEach(myFixture::launchAction)
}
protected fun getQuickFixes(myFixture: CodeInsightTestFixture, regex: Regex, expectedFixes: Int): List<IntentionAction> {
val quickfixes = myFixture.getAllQuickFixes().filter { it.text.matches(regex) }
assertThat(quickfixes).`as`("Fixes matched by $regex: ${myFixture.getAllQuickFixes().map { it.text }}").hasSize(expectedFixes)
return quickfixes
}
}
@@ -0,0 +1,211 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.MyFixture
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
internal class M68kDeadWriteInspectionTest : AbstractInspectionTest() {
@Test
internal fun find_direct_dead_write_to_register(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.l #123,d1
add.l d0,d2
moveq.l #123,d1
"""
)
assertHighlightings(myFixture, 1, "Register d1 is overwritten later without being used")
}
@Test
internal fun find_direct_dead_write_to_register_with_modification(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.l #123,d1
add.l d0,d1
moveq.l #123,d1
"""
)
assertHighlightings(myFixture, 1, "Register d1 is overwritten later without being used")
}
@Test
internal fun no_dead_write_to_register_due_to_part_modification(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.l #123,d1
add.b d1,d0
moveq.l #123,d1
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun use_of_condition_code_causes_weak_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.l #123,d1
scc d0
moveq.l #123,d1
"""
)
assertHighlightings(myFixture, 1, "Register d1 is overwritten later (only CC evaluated?)")
}
@Test
internal fun use_of_conditional_instruction_for_non_conditional_causes_full_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.l #123,d1
st d0
moveq.l #123,d1
"""
)
assertHighlightings(myFixture, 1, "Register d1 is overwritten later without being used")
}
@Test
internal fun use_of_condition_code_after_modification_causes_no_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.l #123,d1
add.b d0,d1
seq .foo
moveq.l #123,d1
.foo
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun use_of_control_flow_causes_no_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.l #123,d1
add.b d0,d1
bra.s .foo
moveq.l #123,d1
.foo
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun movem_can_cause_multiple_warnings(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
movem.l (a0),d0-d7
add.l d0,d1
moveq.l #123,d3
clr.l #123,d6
movem.l (a0),d5-d7
"""
)
assertHighlightings(myFixture, 1, "Register d5 is overwritten later without being used")
assertHighlightings(myFixture, 1, "Register d6 is overwritten later without being used")
assertHighlightings(myFixture, 1, "Register d7 is overwritten later without being used")
}
@Test
internal fun overwrite_in_same_instruction_with_read_causes_no_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.w #123,d3
move.w (a0,d3.w),d3
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun partial_overwrite_in_causes_no_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.w #123,d3
move.b d2,d3
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun oversized_overwrite_in_causes_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.w #123,d3
move.l d2,d3
"""
)
assertHighlightings(myFixture, 1, "Register d3 is overwritten later without being used")
}
@Test
internal fun conditional_write_does_not_cause_a_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
IF 0
move.w #123,d3
ELSE
clr.w d3
ENDC
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun modification_extending_width_does_not_cause_a_warning_on_smaller_write_later(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
move.b (a0)+,d0
lsl #8,d0
move.b (a0)+,d0
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun macro_call_with_register_name_will_not_cause_a_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
moveq.l #-1,d0
COPRMOVE d0,bltafwm
move.l pd_CurrPlanesPtr(a4),d0
"""
)
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun macro_call_with_different_register_name_will_cause_a_warning(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kDeadWriteInspection::class.java)
myFixture.configureByText(
"deadwrite.asm", """
moveq.l #-1,d0
COPRMOVE d1,bltafwm
move.l pd_CurrPlanesPtr(a4),d0
"""
)
assertHighlightings(myFixture, 1, "Register d0 is overwritten later without being used")
}
}
@@ -0,0 +1,70 @@
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 M68kInspectionSuppressorTest : AbstractInspectionTest() {
companion object {
private const val SUPPRESS_FOR_STATEMENT = "Suppress for statement"
}
@Test
internal fun suppresses_inspection_with_end_of_line_comment(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " rts d0 ; suppress M68kSyntax")
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun suppresses_inspection_with_full_line_comment_on_line_before(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", "; suppress M68kSyntax\n rts d0")
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun suppression_works_without_prior_comment(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " rts<caret> d0")
executeSuppressAction(myFixture, SUPPRESS_FOR_STATEMENT)
myFixture.checkResult(" rts d0\t; suppress M68kSyntax")
}
@Test
internal fun suppression_works_with_prior_suppression_comment(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " rts<caret> d0; suppress foobar")
executeSuppressAction(myFixture, SUPPRESS_FOR_STATEMENT)
myFixture.checkResult(" rts d0; suppress foobar M68kSyntax")
}
@Test
internal fun suppression_works_with_prior_suppression_comment_in_line_above(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", "; suppress foobar\n rts<caret> d0")
executeSuppressAction(myFixture, SUPPRESS_FOR_STATEMENT)
myFixture.checkResult("; suppress foobar M68kSyntax\n rts d0")
}
@Test
internal fun suppression_works_by_adding_suppression_comment_in_line_above(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " rts<caret> d0 ; unrelated comment")
executeSuppressAction(myFixture, SUPPRESS_FOR_STATEMENT)
myFixture.checkResult("; suppress M68kSyntax\n rts d0 ; unrelated comment")
}
private fun executeSuppressAction(myFixture: CodeInsightTestFixture, suppressAction: String) {
val highlightInfos = myFixture.doHighlighting()
assertThat(highlightInfos).hasSize(1)
val quickFixPair = highlightInfos[0].quickFixActionRanges[0]
val intentionActionDescriptor = quickFixPair.first
val element = myFixture.file.findElementAt(quickFixPair.second.startOffset)!!
val suppressQuickFix = intentionActionDescriptor.getOptions(element, myFixture.editor)!!
.first { it.text == suppressAction }
myFixture.launchAction(suppressQuickFix)
}
}
@@ -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)")
}
}
@@ -0,0 +1,29 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.MyFixture
import org.junit.jupiter.api.Test
internal class M68kUnresolvedReferenceInspectionTest : AbstractInspectionTest() {
@Test
internal fun shows_warning_on_undefined_symbol_label(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kUnresolvedReferenceInspection::class.java)
myFixture.configureByText("unresolvedref.asm", " bra foobar")
assertHighlightings(myFixture, 1, "Cannot resolve symbol 'foobar'")
}
@Test
internal fun shows_warning_on_undefined_local_label(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kUnresolvedReferenceInspection::class.java)
myFixture.configureByText("unresolvedref.asm", " bra .foobar")
assertHighlightings(myFixture, 1, "Cannot resolve local label '.foobar'")
}
@Test
internal fun shows_warning_on_undefined_macro(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kUnresolvedReferenceInspection::class.java)
myFixture.configureByText("unresolvedref.asm", " foobar")
assertHighlightings(myFixture, 1, "Cannot resolve macro 'foobar'")
}
}
@@ -87,6 +87,11 @@ internal class AddressingModesTest : AbstractParsingTest() {
testGoodSyntax(testCase, " move.l 4.w,a6\n")
}
@Test
internal fun btst_quirk(@MyTestCase testCase: ParsingTestExtension.IParsingTestCase) {
testGoodSyntax(testCase, " btst #5,#42\n btst d0,#42")
}
@Test
internal fun movem_register_list(@MyTestCase testCase: ParsingTestExtension.IParsingTestCase) {
testGoodSyntax(testCase, " movem.l d0-d2/d4/a0/a2-a3/a5,-(sp)\n")
@@ -118,7 +123,7 @@ internal class AddressingModesTest : AbstractParsingTest() {
internal fun special_register_move(@MyTestCase testCase: ParsingTestExtension.IParsingTestCase) {
testGoodSyntax(
testCase, " move.l usp,a0\n"
+ " move.l a5,usp\n"
+ " move.l a5,USP\n"
)
}
@@ -0,0 +1,24 @@
package de.platon42.intellij.plugins.m68k.parser
import de.platon42.intellij.jupiter.MyTestCase
import de.platon42.intellij.jupiter.ParsingTestExtension
import de.platon42.intellij.jupiter.TestDataSubPath
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
@TestDataSubPath("directives")
internal class EndDirectiveTest : AbstractParsingTest() {
@Test
@Disabled("END directive detection breaks stuff, need to find a different solution")
internal fun end_directive(@MyTestCase testCase: ParsingTestExtension.IParsingTestCase) {
testGoodSyntax(
testCase, """
moveq.l #0,d0
rts
END narf
äöö#öä235r2352ou485742q3535p3ü
"""
)
}
}
@@ -22,4 +22,14 @@ internal class OtherDirectivesTest : AbstractParsingTest() {
internal fun if_defined_block(@MyTestCase testCase: ParsingTestExtension.IParsingTestCase) {
testGoodSyntax(testCase, "\tIFD DEBUG ; cause a crash\n illegal\n ENDC\n")
}
@Test
internal fun kalms_include_file_special_quote(@MyTestCase testCase: ParsingTestExtension.IParsingTestCase) {
testGoodSyntax(testCase, " include\t<Engine/A500/System/Copper.i>")
}
@Test
internal fun opt_directive(@MyTestCase testCase: ParsingTestExtension.IParsingTestCase) {
testGoodSyntax(testCase, " opt o+,w-")
}
}
@@ -0,0 +1,57 @@
package de.platon42.intellij.plugins.m68k.refs
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.LightCodeInsightExtension
import de.platon42.intellij.jupiter.MyFixture
import de.platon42.intellij.jupiter.TestDataPath
import de.platon42.intellij.jupiter.TestDataSubPath
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@TestDataPath("src/test/resources/references")
@TestDataSubPath("completion")
@ExtendWith(LightCodeInsightExtension::class)
internal class M68kLocalLabelDefCompletionContributorTest {
@Test
internal fun completion_shows_undefined_local_labels_without_dot_prefix(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByText(
"completeme.asm", """
main
.loop
dbra d0,.loop
dbra d1,.loop2
.<caret>
bra.s .foo
rts
coolsubroutine
bra.s .narf
"""
)
myFixture.completeBasic()
assertThat(myFixture.lookupElementStrings).containsExactlyInAnyOrder("loop2", "foo")
}
@Test
internal fun completion_shows_undefined_local_labels_even_without_dot(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByText(
"completeme.asm", """
main
.loop
dbra d0,.loop
dbra d1,.loop2
<caret>
bra.s .foo
bra.s bar$
rts
coolsubroutine
bra.s .narf
"""
)
myFixture.completeBasic()
assertThat(myFixture.lookupElementStrings).containsExactlyInAnyOrder(".loop2", ".foo", "bar$")
}
}
@@ -2,6 +2,8 @@ package de.platon42.intellij.plugins.m68k.refs
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.psi.PsiElement
import com.intellij.psi.search.FilenameIndex
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.LightCodeInsightExtension
import de.platon42.intellij.jupiter.MyFixture
@@ -86,4 +88,44 @@ internal class M68kReferenceContributorTest : AbstractM68kTest() {
myFixture.checkResultByFile("macros_after_rename.asm")
}
@Test
internal fun reference_to_file_can_be_renamed(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.addFileToProject("otherfile.asm", "; oh no!")
val file = myFixture.configureByText(
"fileref.asm", """
include "otherfil<caret>e.asm"
"""
)
val reference = file.findReferenceAt(myFixture.editor.caretModel.offset)
assertThat(reference).isInstanceOf(M68kIncludeFileReference::class.java)
val otherfile = reference!!.resolve() as M68kFile
assertThat(otherfile.text).isEqualTo("; oh no!")
myFixture.renameElementAtCaret("foobar.asm")
val files = FilenameIndex.getFilesByName(myFixture.project, "foobar.asm", GlobalSearchScope.allScope(myFixture.project))
assertThat(files).hasSize(1)
assertThat(file.text).isEqualToIgnoringWhitespace("include \"foobar.asm\"")
}
@Test
internal fun reference_to_file_in_a_subdirectory_can_be_renamed(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.addFileToProject("foobar/otherfile.asm", "; oh no!")
val file = myFixture.configureByText(
"fileref.asm", """
include "foobar/otherfil<caret>e.asm"
"""
)
val reference = file.findReferenceAt(myFixture.editor.caretModel.offset)
assertThat(reference).isInstanceOf(M68kIncludeFileReference::class.java)
val otherfile = reference!!.resolve() as M68kFile
assertThat(otherfile.text).isEqualTo("; oh no!")
myFixture.renameElementAtCaret("foobar.asm")
val files = FilenameIndex.getFilesByName(myFixture.project, "foobar.asm", GlobalSearchScope.allScope(myFixture.project))
assertThat(files).hasSize(1)
assertThat(file.text).isEqualToIgnoringWhitespace("include \"foobar/foobar.asm\"")
}
}
@@ -0,0 +1,37 @@
package de.platon42.intellij.plugins.m68k.scanner
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.impl.include.FileIncludeManager
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.LightCodeInsightExtension
import de.platon42.intellij.jupiter.MyFixture
import de.platon42.intellij.plugins.m68k.AbstractM68kTest
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@ExtendWith(LightCodeInsightExtension::class)
internal class M68kIncludeFileProviderTest : AbstractM68kTest() {
@Test
internal fun accepts_a_couple_of_include_statements(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.addFileToProject("macros.i", "PUSHM MACRO\n ENDM")
myFixture.addFileToProject("exec/execbase.i", "; Commodore include")
myFixture.addFileToProject("exec/exec.i", " include execbase.i")
myFixture.addFileToProject("includes/hardware/custom.i", "; Another Commodore include")
myFixture.addFileToProject("data/ease_x.bin", "binary file")
myFixture.addFileToProject("fx/fx_angle_only.S", "; Nosfe!!")
myFixture.configureByText(
"completeme.asm", """
include macros.i
include "exec/exec.i"
include <../src/includes/hardware/custom.i>
incbin "data/ease_x.bin"
include fx\fx_angle_only.S
"""
)
val includedFiles = FileIncludeManager.getManager(myFixture.project).getIncludedFiles(myFixture.file.virtualFile, true, true)
assertThat(includedFiles).extracting<String>(VirtualFile::getName)
.containsExactlyInAnyOrder("completeme.asm", "macros.i", "fx_angle_only.S", "custom.i", "exec.i", "execbase.i")
}
}
@@ -0,0 +1,31 @@
Assembly File: a.asm
PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT)
M68kAsmInstructionImpl(ASM_INSTRUCTION)
M68kAsmOpImpl(ASM_OP)
PsiElement(M68kTokenType.MNEMONIC)('btst')
PsiWhiteSpace(' ')
M68kImmediateDataImpl(IMMEDIATE_DATA)
PsiElement(M68kTokenType.HASH)('#')
M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.DECIMAL)('5')
PsiElement(M68kTokenType.SEPARATOR)(',')
M68kImmediateDataImpl(IMMEDIATE_DATA)
PsiElement(M68kTokenType.HASH)('#')
M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.DECIMAL)('42')
PsiElement(M68kTokenType.EOL)('\n')
PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT)
M68kAsmInstructionImpl(ASM_INSTRUCTION)
M68kAsmOpImpl(ASM_OP)
PsiElement(M68kTokenType.MNEMONIC)('btst')
PsiWhiteSpace(' ')
M68kDataRegisterDirectAddressingModeImpl(DATA_REGISTER_DIRECT_ADDRESSING_MODE)
M68kDataRegisterImpl(DATA_REGISTER)
PsiElement(M68kTokenType.DREG)('d0')
PsiElement(M68kTokenType.SEPARATOR)(',')
M68kImmediateDataImpl(IMMEDIATE_DATA)
PsiElement(M68kTokenType.HASH)('#')
M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.DECIMAL)('42')
@@ -29,5 +29,5 @@ Assembly File: a.asm
PsiElement(M68kTokenType.SEPARATOR)(',')
M68kSpecialRegisterDirectAddressingModeImpl(SPECIAL_REGISTER_DIRECT_ADDRESSING_MODE)
M68kSpecialRegisterImpl(SPECIAL_REGISTER)
PsiElement(M68kTokenType.REG_USP)('usp')
PsiElement(M68kTokenType.REG_USP)('USP')
PsiElement(M68kTokenType.EOL)('\n')
@@ -1,11 +1,12 @@
Assembly File: a.asm
M68kStatementImpl(STATEMENT)
M68kGlobalLabelImpl(GLOBAL_LABEL)
PsiElement(M68kTokenType.GLOBAL_LABEL_DEF)('foo')
PsiElement(M68kTokenType.COLON)(':')
PsiWhiteSpace(' ')
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
M68kGlobalLabelImpl(GLOBAL_LABEL)
PsiElement(M68kTokenType.GLOBAL_LABEL_DEF)('foo')
PsiElement(M68kTokenType.COLON)(':')
PsiWhiteSpace(' ')
PsiElement(M68kTokenType.DATA_DIRECTIVE)('dc.b')
M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.DATA_DIRECTIVE)('dc.b')
PsiWhiteSpace(' ')
M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.STRINGLIT)(''It could be good!'')
@@ -2,7 +2,8 @@ Assembly File: a.asm
PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
PsiElement(M68kTokenType.DATA_DIRECTIVE)('dc.w')
M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.DATA_DIRECTIVE)('dc.w')
PsiWhiteSpace(' ')
M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.DECIMAL)('0')
@@ -0,0 +1,26 @@
Assembly File: a.asm
PsiWhiteSpace('\n')
PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT)
M68kAsmInstructionImpl(ASM_INSTRUCTION)
M68kAsmOpImpl(ASM_OP)
PsiElement(M68kTokenType.MNEMONIC)('moveq')
M68kOperandSizeImpl(OPERAND_SIZE)
PsiElement(M68kTokenType.OPSIZE_L)('.l')
PsiWhiteSpace(' ')
M68kImmediateDataImpl(IMMEDIATE_DATA)
PsiElement(M68kTokenType.HASH)('#')
M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.DECIMAL)('0')
PsiElement(M68kTokenType.SEPARATOR)(',')
M68kDataRegisterDirectAddressingModeImpl(DATA_REGISTER_DIRECT_ADDRESSING_MODE)
M68kDataRegisterImpl(DATA_REGISTER)
PsiElement(M68kTokenType.DREG)('d0')
PsiElement(M68kTokenType.EOL)('\n')
PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT)
M68kAsmInstructionImpl(ASM_INSTRUCTION)
M68kAsmOpImpl(ASM_OP)
PsiElement(M68kTokenType.MNEMONIC)('rts')
PsiElement(M68kTokenType.EOL)('\n')
PsiWhiteSpace(' END narf\n äöö#öä235r2352ou485742q3535p3ü\n')
@@ -2,7 +2,8 @@ Assembly File: a.asm
PsiWhiteSpace('\t')
M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
PsiElement(M68kTokenType.DATA_DIRECTIVE)('even')
M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.DATA_DIRECTIVE)('even')
PsiWhiteSpace(' ')
PsiComment(M68kTokenType.COMMENT)('; align to even address')
PsiElement(M68kTokenType.EOL)('\n')
@@ -2,7 +2,8 @@ Assembly File: a.asm
PsiWhiteSpace('\t')
M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('IFD')
M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('IFD')
PsiWhiteSpace(' ')
M68kRefExprImpl(REF_EXPR)
M68kSymbolReferenceImpl(SYMBOL_REFERENCE)
@@ -19,5 +20,6 @@ Assembly File: a.asm
PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('ENDC')
M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('ENDC')
PsiElement(M68kTokenType.EOL)('\n')
@@ -2,7 +2,8 @@ Assembly File: a.asm
PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('include')
M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('include')
PsiWhiteSpace(' ')
M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.STRINGLIT)('"exec/execbase.i"')
@@ -1,12 +1,12 @@
Assembly File: a.asm
M68kStatementImpl(STATEMENT)
M68kGlobalLabelImpl(GLOBAL_LABEL)
PsiElement(M68kTokenType.GLOBAL_LABEL_DEF)('howto')
PsiElement(M68kTokenType.COLON)(':')
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
M68kGlobalLabelImpl(GLOBAL_LABEL)
PsiElement(M68kTokenType.GLOBAL_LABEL_DEF)('howto')
PsiElement(M68kTokenType.COLON)(':')
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('incbin')
M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('incbin')
PsiWhiteSpace(' ')
M68kRefExprImpl(REF_EXPR)
M68kSymbolReferenceImpl(SYMBOL_REFERENCE)
PsiElement(M68kTokenType.SYMBOL)('Convertedassets\scroller_howto.raw.BPL')
M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.STRINGLIT)('Convertedassets\scroller_howto.raw.BPL')
PsiElement(M68kTokenType.EOL)('\n')
@@ -0,0 +1,9 @@
Assembly File: a.asm
PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('include')
PsiWhiteSpace('\t')
M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.STRINGLIT)('<Engine/A500/System/Copper.i>')
@@ -0,0 +1,12 @@
Assembly File: a.asm
PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('opt')
PsiWhiteSpace(' ')
M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.STRINGLIT)('o+')
PsiElement(M68kTokenType.SEPARATOR)(',')
M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.STRINGLIT)('w-')
@@ -2,7 +2,8 @@ Assembly File: a.asm
PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
PsiElement(M68kTokenType.DATA_DIRECTIVE)('dc.w')
M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.DATA_DIRECTIVE)('dc.w')
PsiWhiteSpace(' ')
M68kBinarySubExprImpl(BINARY_SUB_EXPR)
M68kRefExprImpl(REF_EXPR)
@@ -2,7 +2,8 @@ Assembly File: a.asm
PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('IF')
M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('IF')
PsiWhiteSpace(' ')
M68kBinaryCmpEqExprImpl(BINARY_CMP_EQ_EXPR)
M68kRefExprImpl(REF_EXPR)