diff options
author | Kartik Agaram <vc@akkartik.com> | 2020-01-31 18:39:27 -0800 |
---|---|---|
committer | Kartik Agaram <vc@akkartik.com> | 2020-01-31 18:55:37 -0800 |
commit | aeac1e061d72442d919b4727a72f6af5fbb983a5 (patch) | |
tree | f99fdc7e54c158fd3f254138e7322292ce356c34 | |
parent | 4bb0b7e93f3131556325039f02f864bd8ae7683c (diff) | |
download | mu-aeac1e061d72442d919b4727a72f6af5fbb983a5.tar.gz |
5966 - document all supported Mu instructions
-rw-r--r-- | README.md | 5 | ||||
-rwxr-xr-x | apps/mu | bin | 102349 -> 102357 bytes | |||
-rw-r--r-- | apps/mu.subx | 18 | ||||
-rw-r--r-- | html/apps/mu.subx.html | 18 | ||||
-rw-r--r-- | mu_instructions | 186 | ||||
-rw-r--r-- | subx_opcodes (renamed from opcodes) | 0 | ||||
-rw-r--r-- | vimrc.vim | 4 |
7 files changed, 209 insertions, 22 deletions
diff --git a/README.md b/README.md index b9ebf52c..d0bb5992 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/apps/mu b/apps/mu index c4f8dbaf..da654f63 100755 --- a/apps/mu +++ b/apps/mu Binary files differdiff --git a/apps/mu.subx b/apps/mu.subx index c7c972ad..ad297dd2 100644 --- a/apps/mu.subx +++ b/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 diff --git a/html/apps/mu.subx.html b/html/apps/mu.subx.html index 4c1682e2..8ac2db8e 100644 --- a/html/apps/mu.subx.html +++ b/html/apps/mu.subx.html @@ -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 diff --git a/mu_instructions b/mu_instructions new file mode 100644 index 00000000..0571fdf0 --- /dev/null +++ b/mu_instructions @@ -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 diff --git a/opcodes b/subx_opcodes index e222df66..e222df66 100644 --- a/opcodes +++ b/subx_opcodes diff --git a/vimrc.vim b/vimrc.vim index c01ab5c5..88952c4b 100644 --- a/vimrc.vim +++ b/vimrc.vim @@ -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 |