5966 - document all supported Mu instructions
This commit is contained in:
parent
4bb0b7e93f
commit
aeac1e061d
|
@ -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.
|
||||
|
|
18
apps/mu.subx
18
apps/mu.subx
|
@ -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
|
||||
|
|
|
@ -5030,7 +5030,7 @@ if ('onhashchange' in window) {
|
|||
<span id="L5048" class="LineNr">5048 </span> <span class="Constant">"or"</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">"81 4/subop/or"</span>/imm32/subx-name
|
||||
<span id="L5051" class="LineNr">5051 </span> <span class="Constant">"81 1/subop/or"</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">"or-with"</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">"81 4/subop/or"</span>/imm32/subx-name
|
||||
<span id="L5063" class="LineNr">5063 </span> <span class="Constant">"81 1/subop/or"</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">"xor"</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">"81 4/subop/xor"</span>/imm32/subx-name
|
||||
<span id="L5124" class="LineNr">5124 </span> <span class="Constant">"81 6/subop/xor"</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">"xor-with"</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">"81 4/subop/xor"</span>/imm32/subx-name
|
||||
<span id="L5136" class="LineNr">5136 </span> <span class="Constant">"81 6/subop/xor"</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">"compare"</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">"39/compare"</span>/imm32/subx-name
|
||||
<span id="L5282" class="LineNr">5282 </span> <span class="Constant">"39/compare->"</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 => 3b/compare-> var2/rm32 var1/r32</span>
|
||||
<span id="L5290" class="LineNr">5290 </span> <span class="subxComment"># compare var1/reg var2 => 3b/compare<- var2/rm32 var1/r32</span>
|
||||
<span id="L5291" class="LineNr">5291 </span> <span class="Constant">"compare"</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">"3b/compare"</span>/imm32/subx-name
|
||||
<span id="L5294" class="LineNr">5294 </span> <span class="Constant">"3b/compare<-"</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">"39/compare *(ebp+0x00000008) 0x00000000/r32"</span> <span class="Constant">"F - test-compare-mem-with-reg"</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">"39/compare-> *(ebp+0x00000008) 0x00000000/r32"</span> <span class="Constant">"F - test-compare-mem-with-reg"</span>)
|
||||
<span id="L7458" class="LineNr">7458 </span> <span class="subxS1Comment"># . epilogue</span>
|
||||
<span id="L7459" class="LineNr">7459 </span> 89/<- %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">"3b/compare *(ebp+0x00000008) 0x00000000/r32"</span> <span class="Constant">"F - test-compare-reg-with-mem"</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">"3b/compare<- *(ebp+0x00000008) 0x00000000/r32"</span> <span class="Constant">"F - test-compare-reg-with-mem"</span>)
|
||||
<span id="L7517" class="LineNr">7517 </span> <span class="subxS1Comment"># . epilogue</span>
|
||||
<span id="L7518" class="LineNr">7518 </span> 89/<- %esp 5/r32/ebp
|
||||
<span id="L7519" class="LineNr">7519 </span> 5d/pop-to-ebp
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue