5966 - document all supported Mu instructions

This commit is contained in:
Kartik Agaram 2020-01-31 18:39:27 -08:00
parent 4bb0b7e93f
commit aeac1e061d
7 changed files with 209 additions and 22 deletions

View File

@ -66,7 +66,8 @@ statements in Mu translate to a single machine code instruction. Variables
reside in memory by default. Programs must specify registers when they want to
use them. Functions must return results in registers. Execution begins at the
function `main`, which always returns its result in register `ebx`. [This post](http://akkartik.name/post/mu-2019-2)
has more details.
has more details. You can see a complete list of supported instructions and
their translations in [this summary](mu_instructions).
## SubX
@ -717,7 +718,7 @@ a) Try running the tests: `./test_apps`
b) Check out the online help. Starting point: `./bootstrap`
c) Familiarize yourself with `./bootstrap help opcodes`. If you program in Mu
you'll spend a lot of time with it. (It's also [in this repo](https://github.com/akkartik/mu/blob/master/opcodes).)
you'll spend a lot of time with it. (It's also [in this repo](https://github.com/akkartik/mu/blob/master/subx_opcodes).)
[Here](https://lobste.rs/s/qglfdp/subx_minimalist_assembly_language_for#c_o9ddqk)
are some tips on my setup for quickly finding the right opcode for any
situation from within Vim.

BIN
apps/mu

Binary file not shown.

View File

@ -5048,7 +5048,7 @@ _Primitive-or-lit-with-reg:
"or"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"81 4/subop/or"/imm32/subx-name
"81 1/subop/or"/imm32/subx-name
3/imm32/rm32-is-first-output
0/imm32/no-r32
1/imm32/imm32-is-first-inout
@ -5060,7 +5060,7 @@ _Primitive-or-lit-with-mem:
"or-with"/imm32/name
Int-var-and-literal/imm32/inouts
0/imm32/outputs
"81 4/subop/or"/imm32/subx-name
"81 1/subop/or"/imm32/subx-name
1/imm32/rm32-is-first-inout
0/imm32/no-r32
2/imm32/imm32-is-second-inout
@ -5121,7 +5121,7 @@ _Primitive-xor-lit-with-reg:
"xor"/imm32/name
Single-lit-var/imm32/inouts
Single-int-var-in-some-register/imm32/outputs
"81 4/subop/xor"/imm32/subx-name
"81 6/subop/xor"/imm32/subx-name
3/imm32/rm32-is-first-output
0/imm32/no-r32
1/imm32/imm32-is-first-inout
@ -5133,7 +5133,7 @@ _Primitive-xor-lit-with-mem:
"xor-with"/imm32/name
Int-var-and-literal/imm32/inouts
0/imm32/outputs
"81 4/subop/xor"/imm32/subx-name
"81 6/subop/xor"/imm32/subx-name
1/imm32/rm32-is-first-inout
0/imm32/no-r32
2/imm32/imm32-is-first-inout
@ -5279,7 +5279,7 @@ _Primitive-compare-mem-with-reg:
"compare"/imm32/name
Two-args-int-stack-int-reg/imm32/inouts
0/imm32/outputs
"39/compare"/imm32/subx-name
"39/compare->"/imm32/subx-name
1/imm32/rm32-is-first-inout
2/imm32/r32-is-second-inout
0/imm32/no-imm32
@ -5287,11 +5287,11 @@ _Primitive-compare-mem-with-reg:
0/imm32/output-is-write-only
_Primitive-compare-reg-with-mem/imm32/next
_Primitive-compare-reg-with-mem:
# compare var1/reg var2 => 3b/compare-> var2/rm32 var1/r32
# compare var1/reg var2 => 3b/compare<- var2/rm32 var1/r32
"compare"/imm32/name
Two-args-int-reg-int-stack/imm32/inouts
0/imm32/outputs
"3b/compare"/imm32/subx-name
"3b/compare<-"/imm32/subx-name
2/imm32/rm32-is-second-inout
1/imm32/r32-is-first-inout
0/imm32/no-imm32
@ -7454,7 +7454,7 @@ test-compare-mem-with-reg:
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "39/compare *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-mem-with-reg")
(check-next-stream-line-equal _test-output-stream "39/compare-> *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-mem-with-reg")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
@ -7513,7 +7513,7 @@ test-compare-reg-with-mem:
#? (rewind-stream _test-output-stream)
#? # }}}
# check output
(check-next-stream-line-equal _test-output-stream "3b/compare *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-reg-with-mem")
(check-next-stream-line-equal _test-output-stream "3b/compare<- *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-reg-with-mem")
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp

View File

@ -5030,7 +5030,7 @@ if ('onhashchange' in window) {
<span id="L5048" class="LineNr">5048 </span> <span class="Constant">&quot;or&quot;</span>/imm32/name
<span id="L5049" class="LineNr">5049 </span> <span class="SpecialChar"><a href='mu.subx.html#L5907'>Single-lit-var</a></span>/imm32/inouts
<span id="L5050" class="LineNr">5050 </span> <span class="SpecialChar"><a href='mu.subx.html#L5830'>Single-int-var-in-some-register</a></span>/imm32/outputs
<span id="L5051" class="LineNr">5051 </span> <span class="Constant">&quot;81 4/subop/or&quot;</span>/imm32/subx-name
<span id="L5051" class="LineNr">5051 </span> <span class="Constant">&quot;81 1/subop/or&quot;</span>/imm32/subx-name
<span id="L5052" class="LineNr">5052 </span> 3/imm32/rm32-is-first-output
<span id="L5053" class="LineNr">5053 </span> 0/imm32/no-r32
<span id="L5054" class="LineNr">5054 </span> 1/imm32/imm32-is-first-inout
@ -5042,7 +5042,7 @@ if ('onhashchange' in window) {
<span id="L5060" class="LineNr">5060 </span> <span class="Constant">&quot;or-with&quot;</span>/imm32/name
<span id="L5061" class="LineNr">5061 </span> <span class="SpecialChar"><a href='mu.subx.html#L5826'>Int-var-and-literal</a></span>/imm32/inouts
<span id="L5062" class="LineNr">5062 </span> 0/imm32/outputs
<span id="L5063" class="LineNr">5063 </span> <span class="Constant">&quot;81 4/subop/or&quot;</span>/imm32/subx-name
<span id="L5063" class="LineNr">5063 </span> <span class="Constant">&quot;81 1/subop/or&quot;</span>/imm32/subx-name
<span id="L5064" class="LineNr">5064 </span> 1/imm32/rm32-is-first-inout
<span id="L5065" class="LineNr">5065 </span> 0/imm32/no-r32
<span id="L5066" class="LineNr">5066 </span> 2/imm32/imm32-is-second-inout
@ -5103,7 +5103,7 @@ if ('onhashchange' in window) {
<span id="L5121" class="LineNr">5121 </span> <span class="Constant">&quot;xor&quot;</span>/imm32/name
<span id="L5122" class="LineNr">5122 </span> <span class="SpecialChar"><a href='mu.subx.html#L5907'>Single-lit-var</a></span>/imm32/inouts
<span id="L5123" class="LineNr">5123 </span> <span class="SpecialChar"><a href='mu.subx.html#L5830'>Single-int-var-in-some-register</a></span>/imm32/outputs
<span id="L5124" class="LineNr">5124 </span> <span class="Constant">&quot;81 4/subop/xor&quot;</span>/imm32/subx-name
<span id="L5124" class="LineNr">5124 </span> <span class="Constant">&quot;81 6/subop/xor&quot;</span>/imm32/subx-name
<span id="L5125" class="LineNr">5125 </span> 3/imm32/rm32-is-first-output
<span id="L5126" class="LineNr">5126 </span> 0/imm32/no-r32
<span id="L5127" class="LineNr">5127 </span> 1/imm32/imm32-is-first-inout
@ -5115,7 +5115,7 @@ if ('onhashchange' in window) {
<span id="L5133" class="LineNr">5133 </span> <span class="Constant">&quot;xor-with&quot;</span>/imm32/name
<span id="L5134" class="LineNr">5134 </span> <span class="SpecialChar"><a href='mu.subx.html#L5826'>Int-var-and-literal</a></span>/imm32/inouts
<span id="L5135" class="LineNr">5135 </span> 0/imm32/outputs
<span id="L5136" class="LineNr">5136 </span> <span class="Constant">&quot;81 4/subop/xor&quot;</span>/imm32/subx-name
<span id="L5136" class="LineNr">5136 </span> <span class="Constant">&quot;81 6/subop/xor&quot;</span>/imm32/subx-name
<span id="L5137" class="LineNr">5137 </span> 1/imm32/rm32-is-first-inout
<span id="L5138" class="LineNr">5138 </span> 0/imm32/no-r32
<span id="L5139" class="LineNr">5139 </span> 2/imm32/imm32-is-first-inout
@ -5261,7 +5261,7 @@ if ('onhashchange' in window) {
<span id="L5279" class="LineNr">5279 </span> <span class="Constant">&quot;compare&quot;</span>/imm32/name
<span id="L5280" class="LineNr">5280 </span> <span class="SpecialChar"><a href='mu.subx.html#L5814'>Two-args-int-stack-int-reg</a></span>/imm32/inouts
<span id="L5281" class="LineNr">5281 </span> 0/imm32/outputs
<span id="L5282" class="LineNr">5282 </span> <span class="Constant">&quot;39/compare&quot;</span>/imm32/subx-name
<span id="L5282" class="LineNr">5282 </span> <span class="Constant">&quot;39/compare-&gt;&quot;</span>/imm32/subx-name
<span id="L5283" class="LineNr">5283 </span> 1/imm32/rm32-is-first-inout
<span id="L5284" class="LineNr">5284 </span> 2/imm32/r32-is-second-inout
<span id="L5285" class="LineNr">5285 </span> 0/imm32/no-imm32
@ -5269,11 +5269,11 @@ if ('onhashchange' in window) {
<span id="L5287" class="LineNr">5287 </span> 0/imm32/output-is-write-only
<span id="L5288" class="LineNr">5288 </span> <a href='mu.subx.html#L5289'>_Primitive-compare-reg-with-mem</a>/imm32/next
<span id="L5289" class="LineNr">5289 </span><span class="subxMinorFunction">_Primitive-compare-reg-with-mem</span>:
<span id="L5290" class="LineNr">5290 </span> <span class="subxComment"># compare var1/reg var2 =&gt; 3b/compare-&gt; var2/rm32 var1/r32</span>
<span id="L5290" class="LineNr">5290 </span> <span class="subxComment"># compare var1/reg var2 =&gt; 3b/compare&lt;- var2/rm32 var1/r32</span>
<span id="L5291" class="LineNr">5291 </span> <span class="Constant">&quot;compare&quot;</span>/imm32/name
<span id="L5292" class="LineNr">5292 </span> <span class="SpecialChar"><a href='mu.subx.html#L5818'>Two-args-int-reg-int-stack</a></span>/imm32/inouts
<span id="L5293" class="LineNr">5293 </span> 0/imm32/outputs
<span id="L5294" class="LineNr">5294 </span> <span class="Constant">&quot;3b/compare&quot;</span>/imm32/subx-name
<span id="L5294" class="LineNr">5294 </span> <span class="Constant">&quot;3b/compare&lt;-&quot;</span>/imm32/subx-name
<span id="L5295" class="LineNr">5295 </span> 2/imm32/rm32-is-second-inout
<span id="L5296" class="LineNr">5296 </span> 1/imm32/r32-is-first-inout
<span id="L5297" class="LineNr">5297 </span> 0/imm32/no-imm32
@ -7371,7 +7371,7 @@ if ('onhashchange' in window) {
<span id="L7449" class="LineNr">7449 </span> (<a href='../064write-byte.subx.html#L81'>flush</a> <a href='../064write-byte.subx.html#L328'>_test-output-buffered-file</a>)
<span id="L7450" class="Folded">7450 </span><span class="Folded">+-- 6 lines: #? # dump _test-output-stream --------------------------------------------------------------------------------------------------------------</span>
<span id="L7456" class="LineNr">7456 </span> <span class="subxComment"># check output</span>
<span id="L7457" class="LineNr">7457 </span> (<a href='../058stream-equal.subx.html#L565'>check-next-stream-line-equal</a> <a href='../064write-byte.subx.html#L286'>_test-output-stream</a> <span class="Constant">&quot;39/compare *(ebp+0x00000008) 0x00000000/r32&quot;</span> <span class="Constant">&quot;F - test-compare-mem-with-reg&quot;</span>)
<span id="L7457" class="LineNr">7457 </span> (<a href='../058stream-equal.subx.html#L565'>check-next-stream-line-equal</a> <a href='../064write-byte.subx.html#L286'>_test-output-stream</a> <span class="Constant">&quot;39/compare-&gt; *(ebp+0x00000008) 0x00000000/r32&quot;</span> <span class="Constant">&quot;F - test-compare-mem-with-reg&quot;</span>)
<span id="L7458" class="LineNr">7458 </span> <span class="subxS1Comment"># . epilogue</span>
<span id="L7459" class="LineNr">7459 </span> 89/&lt;- %esp 5/r32/ebp
<span id="L7460" class="LineNr">7460 </span> 5d/pop-to-ebp
@ -7425,7 +7425,7 @@ if ('onhashchange' in window) {
<span id="L7508" class="LineNr">7508 </span> (<a href='../064write-byte.subx.html#L81'>flush</a> <a href='../064write-byte.subx.html#L328'>_test-output-buffered-file</a>)
<span id="L7509" class="Folded">7509 </span><span class="Folded">+-- 6 lines: #? # dump _test-output-stream --------------------------------------------------------------------------------------------------------------</span>
<span id="L7515" class="LineNr">7515 </span> <span class="subxComment"># check output</span>
<span id="L7516" class="LineNr">7516 </span> (<a href='../058stream-equal.subx.html#L565'>check-next-stream-line-equal</a> <a href='../064write-byte.subx.html#L286'>_test-output-stream</a> <span class="Constant">&quot;3b/compare *(ebp+0x00000008) 0x00000000/r32&quot;</span> <span class="Constant">&quot;F - test-compare-reg-with-mem&quot;</span>)
<span id="L7516" class="LineNr">7516 </span> (<a href='../058stream-equal.subx.html#L565'>check-next-stream-line-equal</a> <a href='../064write-byte.subx.html#L286'>_test-output-stream</a> <span class="Constant">&quot;3b/compare&lt;- *(ebp+0x00000008) 0x00000000/r32&quot;</span> <span class="Constant">&quot;F - test-compare-reg-with-mem&quot;</span>)
<span id="L7517" class="LineNr">7517 </span> <span class="subxS1Comment"># . epilogue</span>
<span id="L7518" class="LineNr">7518 </span> 89/&lt;- %esp 5/r32/ebp
<span id="L7519" class="LineNr">7519 </span> 5d/pop-to-ebp

186
mu_instructions Normal file
View File

@ -0,0 +1,186 @@
## Mu's instructions and their table-driven translation
Mu is a statement-oriented language. Blocks consist of flat lists of instructions.
The chart at the bottom of this page shows all the instruction forms supported
by Mu, one to a line. Each line shows an example of the instruction on the
left. Past the first column, everything inside the {..} is a summary of the
data structure the Mu compiler uses (`Primitives` in apps/mu.subx) to translate
it.
The syntax of the data structure is intended to be similar to C++'s aggregate
initializers (https://en.cppreference.com/w/cpp/language/aggregate_initialization)
However, there are differences:
- We use adjacency for string concatenation.
- We use [] for array literals.
- The objects inside [] are not fully described. They include various
metadata about the variable in the instruction. For our purposes, assume
that variables on the stack have a stack offset computed for them, and
register variables evaluate to their register.
- registers may be specified by name: /eax /ecx /edx /ebx /esi /edi
- registers may be specified as a wildcard: /reg
- integer literals are always called 'n'
- any other variable names that don't specify a register are assumed to be on the stack
There are no checks for types yet, because Mu programs only have `int` types so far.
Example 1 (use the widest screen you can for this page):
-- instruction form -- | -------------------------- data structure ----------------------------
|<------------- pattern matching ---------->|<--- code generation ------------------->
var/reg <- add var2/reg {.name="add", .inouts=[reg], .outputs=[reg], .subx-name="01/add<-", .rm32=outputs[0], .r32=inouts[0]}
Read this as:
if an instruction's name is "add"
and it has one inout that's in a register
and it has one output that's in a register,
then emit the following on a single line
"01/add<-" (the opcode or subx-name)
"%{reg}", interpolating the output's register
"{reg}/r32", interpolating the inout's register code.
Example 2:
-- instruction form -- | -------------------------- data structure ----------------------------
|<------- pattern matching ------>|<--- code generation ------------------->
add-to var, n {.name="add-to", .inouts=[var, n], .subx-name="81 0/subop/add", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]}
Read this as:
if an instruction's name is "add-to"
and it has two inouts
the first on the stack
and the second a literal,
then emit the following on a single line
"81 0/subop/add" (the opcode or subx-name)
"*(ebp+{stack})", interpolating the first inout's stack offset
"{n}/imm32", interpolating the second inout's contents
Ok, here's the complete chart.
-- instruction form -- | -------------------------- data structure ----------------------------
|<------------------- pattern matching ------------------->|<---- code generation ------------------->
var/eax <- increment {.name="increment", .outputs=[eax], .subx-name="40/increment-eax"}
var/ecx <- increment {.name="increment", .outputs=[ecx], .subx-name="41/increment-ecx"}
var/edx <- increment {.name="increment", .outputs=[edx], .subx-name="42/increment-edx"}
var/ebx <- increment {.name="increment", .outputs=[ebx], .subx-name="43/increment-ebx"}
var/esi <- increment {.name="increment", .outputs=[esi], .subx-name="46/increment-esi"}
var/edi <- increment {.name="increment", .outputs=[edi], .subx-name="47/increment-edi"}
increment var {.name="increment", .inouts=[var], .subx-name="ff 0/subop/increment", .rm32="*(ebp+" inouts[0].stack-offset ")"}
var/eax <- decrement {.name="decrement", .outputs=[eax], .subx-name="48/decrement-eax"}
var/ecx <- decrement {.name="decrement", .outputs=[ecx], .subx-name="49/decrement-ecx"}
var/edx <- decrement {.name="decrement", .outputs=[edx], .subx-name="4a/decrement-edx"}
var/ebx <- decrement {.name="decrement", .outputs=[ebx], .subx-name="4b/decrement-ebx"}
var/esi <- decrement {.name="decrement", .outputs=[esi], .subx-name="4e/decrement-esi"}
var/edi <- decrement {.name="decrement", .outputs=[edi], .subx-name="4f/decrement-edi"}
decrement var {.name="decrement", .inouts=[var], .subx-name="ff 1/subop/decrement", .rm32="*(ebp+" inouts[0].stack-offset ")"}
var1/reg1 <- add var2/reg2 {.name="add", .inouts=[reg2], .outputs=[reg1], .subx-name="01/add<-", .rm32=outputs[0], .r32=inouts[0]}
var/reg <- add var2 {.name="add", .inouts=[var2], .outputs=[reg], .subx-name="03/add->", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=outputs[0]}
add-to var1, var2/reg {.name="add-to", .inouts=[var1, var2], .subx-name="01/add<-", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=inouts[1]}
var/eax <- add n {.name="add", .inouts=[n], .outputs=[eax], .subx-name="05/add-to-eax", .imm32=inouts[0]}
var/reg <- add n {.name="add", .inouts=[n], .outputs=[reg], .subx-name="81 0/subop/add", .rm32=outputs[0], .imm32=inouts[0]}
add-to var, n {.name="add-to", .inouts=[var, n], .subx-name="81 0/subop/add", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]}
var1/reg1 <- sub var2/reg2 {.name="sub", .inouts=[reg2], .outputs=[reg1], .subx-name="29/sub<-", .rm32=outputs[0], .r32=inouts[0]}
var/reg <- sub var2 {.name="sub", .inouts=[var2], .outputs=[reg], .subx-name="2b/sub->", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=outputs[0]}
sub-from var1, var2/reg {.name="sub-from", .inouts=[var1, var2], .subx-name="29/sub<-", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=inouts[1]}
var/eax <- sub n {.name="sub", .inouts=[n], .outputs=[eax], .subx-name="2d/sub-from-eax", .imm32=inouts[0]}
var/reg <- sub n {.name="sub", .inouts=[n], .outputs=[reg], .subx-name="81 5/subop/subtract", .rm32=outputs[0], .imm32=inouts[0]}
sub-from var, n {.name="sub-from", .inouts=[var, n], .subx-name="81 5/subop/subtract", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]}
var1/reg1 <- and var2/reg2 {.name="and", .inouts=[reg2], .outputs=[reg1], .subx-name="21/and<-", .rm32=outputs[0], .r32=inouts[0]}
var/reg <- and var2 {.name="and", .inouts=[var2], .outputs=[reg], .subx-name="23/and->", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=outputs[0]}
and-with var1, var2/reg {.name="and-with", .inouts=[var1, reg], .subx-name="21/and<-", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=inouts[1]}
var/eax <- and n {.name="and", .inouts=[n], .outputs=[eax], .subx-name="25/and-with-eax", .imm32=inouts[0]}
var/reg <- and n {.name="and", .inouts=[n], .outputs=[reg], .subx-name="81 4/subop/and", .rm32=outputs[0], .imm32=inouts[0]}
and-with var, n {.name="and-with", .inouts=[var, n], .subx-name="81 4/subop/and", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]}
var1/reg1 <- or var2/reg2 {.name="or", .inouts=[reg2], .outputs=[reg1], .subx-name="09/or<-", .rm32=outputs[0], .r32=inouts[0]}
var/reg <- or var2 {.name="or", .inouts=[var2], .outputs=[reg], .subx-name="0b/or->", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=outputs[0]}
or-with var1, var2/reg {.name="or-with", .inouts=[var1, reg], .subx-name="09/or<-", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=inouts[1]}
var/eax <- or n {.name="or", .inouts=[n], .outputs=[eax], .subx-name="0d/or-with-eax", .imm32=inouts[0]}
var/reg <- or n {.name="or", .inouts=[n], .outputs=[reg], .subx-name="81 1/subop/or", .rm32=outputs[0], .imm32=inouts[0]}
or-with var, n {.name="or-with", .inouts=[var, n], .subx-name="81 1/subop/or", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]}
var1/reg1 <- xor var2/reg2 {.name="xor", .inouts=[reg2], .outputs=[reg1], .subx-name="31/xor<-", .rm32=outputs[0], .r32=inouts[0]}
var/reg <- xor var2 {.name="xor", .inouts=[var2], .outputs=[reg], .subx-name="33/xor->", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=outputs[0]}
xor-with var1, var2/reg {.name="xor-with", .inouts=[var1, reg], .subx-name="31/xor<-", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=inouts[1]}
var/eax <- xor n {.name="xor", .inouts=[n], .outputs=[eax], .subx-name="35/xor-with-eax", .imm32=inouts[0]}
var/reg <- xor n {.name="xor", .inouts=[n], .outputs=[reg], .subx-name="81 6/subop/xor", .rm32=outputs[0], .imm32=inouts[0]}
xor-with var, n {.name="xor-with", .inouts=[var, n], .subx-name="81 6/subop/xor", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]}
var/eax <- copy n {.name="copy", .inouts=[n], .outputs=[eax], .subx-name="b8/copy-to-eax", .imm32=inouts[0]}
var/ecx <- copy n {.name="copy", .inouts=[n], .outputs=[ecx], .subx-name="b9/copy-to-ecx", .imm32=inouts[0]}
var/edx <- copy n {.name="copy", .inouts=[n], .outputs=[edx], .subx-name="ba/copy-to-edx", .imm32=inouts[0]}
var/ebx <- copy n {.name="copy", .inouts=[n], .outputs=[ebx], .subx-name="bb/copy-to-ebx", .imm32=inouts[0]}
var/esi <- copy n {.name="copy", .inouts=[n], .outputs=[esi], .subx-name="be/copy-to-esi", .imm32=inouts[0]}
var/edi <- copy n {.name="copy", .inouts=[n], .outputs=[edi], .subx-name="bf/copy-to-edi", .imm32=inouts[0]}
var1/reg1 <- copy var2/reg2 {.name="copy", .inouts=[reg2], .outputs=[reg1], .subx-name="89/copy-to", .rm32=outputs[0], .r32=inouts[0]}
copy-to var1, var2/reg {.name="copy-to", .inouts=[var1, var2], .subx-name="01/add<-", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=inouts[1]}
var/reg <- copy var2 {.name="copy", .inouts=[var2], .outputs=[reg], .subx-name="8b/copy-from", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=outputs[0]}
var/reg <- copy n {.name="copy", .inouts=[n], .outputs=[reg], .subx-name="c7 0/subop/copy", .rm32=outputs[0], .imm32=inouts[0]}
copy-to var, n {.name="copy-to", .inouts=[var, n], .subx-name="c7 0/subop/copy", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]}
compare var1, var2/reg {.name="compare", .inouts=[var1, reg], .subx-name="39/compare->", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=inouts[1]}
compare var1/reg, var2 {.name="compare", .inouts=[reg, var2], .subx-name="3b/compare<-", .rm32="*(ebp+" inouts[1].stack-offset ")", .r32=inouts[0]}
compare var/eax, n {.name="compare", .inouts=[eax, n], .subx-name="3d/compare-eax-with", .imm32=inouts[1]}
compare var, n {.name="compare", .inouts=[var, n], .subx-name="81 7/subop/compare", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]}
var/reg <- multiply var2 {.name="multiply", .inouts=[var2], .outputs=[reg], .subx-name="0f af/multiply", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=outputs[0]}
Jumps have a slightly simpler format. Most of the time they take no inouts or
outputs. Occasionally you give them a label for a block to jump to the start
or end of.
break-if-= {.name="break-if-=", .subx-name="0f 84/jump-if-= break/disp32"}
break-if-= label {.name="break-if-=", .inouts=[label], .subx-name="0f 84/jump-if-=", .disp32=inouts[0] ":break"}
break-if-!= {.name="break-if-!=", .subx-name="0f 85/jump-if-!= break/disp32"}
break-if-!= label {.name="break-if-!=", .inouts=[label], .subx-name="0f 85/jump-if-!=", .disp32=inouts[0] ":break"}
Inequalities are similar, but have unsigned and signed variants. We assume
unsigned variants are only ever used to compare addresses.
break-if-addr< {.name="break-if-addr<", .subx-name="0f 82/jump-if-addr< break/disp32"}
break-if-addr< label {.name="break-if-addr<", .inouts=[label], .subx-name="0f 82/jump-if-addr<", .disp32=inouts[0] ":break"}
break-if-addr> {.name="break-if-addr>", .subx-name="0f 87/jump-if-addr> break/disp32"}
break-if-addr> label {.name="break-if-addr>", .inouts=[label], .subx-name="0f 87/jump-if-addr>", .disp32=inouts[0] ":break"}
break-if-addr<= {.name="break-if-addr<=", .subx-name="0f 86/jump-if-addr<= break/disp32"}
break-if-addr<= label {.name="break-if-addr<=", .inouts=[label], .subx-name="0f 86/jump-if-addr<=", .disp32=inouts[0] ":break"}
break-if-addr>= {.name="break-if-addr>=", .subx-name="0f 83/jump-if-addr>= break/disp32"}
break-if-addr>= label {.name="break-if-addr>=", .inouts=[label], .subx-name="0f 83/jump-if-addr>=", .disp32=inouts[0] ":break"}
break-if-< {.name="break-if-<", .subx-name="0f 8c/jump-if-< break/disp32"}
break-if-< label {.name="break-if-<", .inouts=[label], .subx-name="0f 8c/jump-if-<", .disp32=inouts[0] ":break"}
break-if-> {.name="break-if->", .subx-name="0f 8f/jump-if-> break/disp32"}
break-if-> label {.name="break-if->", .inouts=[label], .subx-name="0f 8f/jump-if->", .disp32=inouts[0] ":break"}
break-if-<= {.name="break-if-<=", .subx-name="0f 8e/jump-if-<= break/disp32"}
break-if-<= label {.name="break-if-<=", .inouts=[label], .subx-name="0f 8e/jump-if-<=", .disp32=inouts[0] ":break"}
break-if->= {.name="break-if->=", .subx-name="0f 8d/jump-if->= break/disp32"}
break-if->= label {.name="break-if->=", .inouts=[label], .subx-name="0f 8d/jump-if->=", .disp32=inouts[0] ":break"}
Finally, we repeat all the 'break' variants almost identically for 'loop'
instructions. This works because the compiler inserts ':loop' labels at the
start of such named blocks, and ':break' labels at the end.
loop-if-= {.name="loop-if-=", .subx-name="0f 84/jump-if-= loop/disp32"}
loop-if-= label {.name="loop-if-=", .inouts=[label], .subx-name="0f 84/jump-if-=", .disp32=inouts[0] ":loop"}
loop-if-!= {.name="loop-if-!=", .subx-name="0f 85/jump-if-!= loop/disp32"}
loop-if-!= label {.name="loop-if-!=", .inouts=[label], .subx-name="0f 85/jump-if-!=", .disp32=inouts[0] ":loop"}
loop-if-addr< {.name="loop-if-addr<", .subx-name="0f 82/jump-if-addr< loop/disp32"}
loop-if-addr< label {.name="loop-if-addr<", .inouts=[label], .subx-name="0f 82/jump-if-addr<", .disp32=inouts[0] ":loop"}
loop-if-addr> {.name="loop-if-addr>", .subx-name="0f 87/jump-if-addr> loop/disp32"}
loop-if-addr> label {.name="loop-if-addr>", .inouts=[label], .subx-name="0f 87/jump-if-addr>", .disp32=inouts[0] ":loop"}
loop-if-addr<= {.name="loop-if-addr<=", .subx-name="0f 86/jump-if-addr<= loop/disp32"}
loop-if-addr<= label {.name="loop-if-addr<=", .inouts=[label], .subx-name="0f 86/jump-if-addr<=", .disp32=inouts[0] ":loop"}
loop-if-addr>= {.name="loop-if-addr>=", .subx-name="0f 83/jump-if-addr>= loop/disp32"}
loop-if-addr>= label {.name="loop-if-addr>=", .inouts=[label], .subx-name="0f 83/jump-if-addr>=", .disp32=inouts[0] ":loop"}
loop-if-< {.name="loop-if-<", .subx-name="0f 8c/jump-if-< loop/disp32"}
loop-if-< label {.name="loop-if-<", .inouts=[label], .subx-name="0f 8c/jump-if-<", .disp32=inouts[0] ":loop"}
loop-if-> {.name="loop-if->", .subx-name="0f 8f/jump-if-> loop/disp32"}
loop-if-> label {.name="loop-if->", .inouts=[label], .subx-name="0f 8f/jump-if->", .disp32=inouts[0] ":loop"}
loop-if-<= {.name="loop-if-<=", .subx-name="0f 8e/jump-if-<= loop/disp32"}
loop-if-<= label {.name="loop-if-<=", .inouts=[label], .subx-name="0f 8e/jump-if-<=", .disp32=inouts[0] ":loop"}
loop-if->= {.name="loop-if->=", .subx-name="0f 8d/jump-if->= loop/disp32"}
loop-if->= label {.name="loop-if->=", .inouts=[label], .subx-name="0f 8d/jump-if->=", .disp32=inouts[0] ":loop"}
vim:ft=c:nowrap

View File

@ -56,9 +56,9 @@ endfunction
command! -nargs=1 G call GrepSubX(<q-args>)
if exists("&splitvertical")
command! -nargs=0 P hor split opcodes
command! -nargs=0 P hor split subx_opcodes
else
command! -nargs=0 P split opcodes
command! -nargs=0 P split subx_opcodes
endif
" useful for inspecting just the control flow in a trace