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
Binary files a/apps/mu and b/apps/mu differ
diff --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) {
5048 "or"/imm32/name
5049 Single-lit-var/imm32/inouts
5050 Single-int-var-in-some-register/imm32/outputs
-5051 "81 4/subop/or"/imm32/subx-name
+5051 "81 1/subop/or"/imm32/subx-name
5052 3/imm32/rm32-is-first-output
5053 0/imm32/no-r32
5054 1/imm32/imm32-is-first-inout
@@ -5042,7 +5042,7 @@ if ('onhashchange' in window) {
5060 "or-with"/imm32/name
5061 Int-var-and-literal/imm32/inouts
5062 0/imm32/outputs
-5063 "81 4/subop/or"/imm32/subx-name
+5063 "81 1/subop/or"/imm32/subx-name
5064 1/imm32/rm32-is-first-inout
5065 0/imm32/no-r32
5066 2/imm32/imm32-is-second-inout
@@ -5103,7 +5103,7 @@ if ('onhashchange' in window) {
5121 "xor"/imm32/name
5122 Single-lit-var/imm32/inouts
5123 Single-int-var-in-some-register/imm32/outputs
-5124 "81 4/subop/xor"/imm32/subx-name
+5124 "81 6/subop/xor"/imm32/subx-name
5125 3/imm32/rm32-is-first-output
5126 0/imm32/no-r32
5127 1/imm32/imm32-is-first-inout
@@ -5115,7 +5115,7 @@ if ('onhashchange' in window) {
5133 "xor-with"/imm32/name
5134 Int-var-and-literal/imm32/inouts
5135 0/imm32/outputs
-5136 "81 4/subop/xor"/imm32/subx-name
+5136 "81 6/subop/xor"/imm32/subx-name
5137 1/imm32/rm32-is-first-inout
5138 0/imm32/no-r32
5139 2/imm32/imm32-is-first-inout
@@ -5261,7 +5261,7 @@ if ('onhashchange' in window) {
5279 "compare"/imm32/name
5280 Two-args-int-stack-int-reg/imm32/inouts
5281 0/imm32/outputs
-5282 "39/compare"/imm32/subx-name
+5282 "39/compare->"/imm32/subx-name
5283 1/imm32/rm32-is-first-inout
5284 2/imm32/r32-is-second-inout
5285 0/imm32/no-imm32
@@ -5269,11 +5269,11 @@ if ('onhashchange' in window) {
5287 0/imm32/output-is-write-only
5288 _Primitive-compare-reg-with-mem/imm32/next
5289 _Primitive-compare-reg-with-mem:
-5290
+5290
5291 "compare"/imm32/name
5292 Two-args-int-reg-int-stack/imm32/inouts
5293 0/imm32/outputs
-5294 "3b/compare"/imm32/subx-name
+5294 "3b/compare<-"/imm32/subx-name
5295 2/imm32/rm32-is-second-inout
5296 1/imm32/r32-is-first-inout
5297 0/imm32/no-imm32
@@ -7371,7 +7371,7 @@ if ('onhashchange' in window) {
7449 (flush _test-output-buffered-file)
7450 +-- 6 lines: #? # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7456
-7457 (check-next-stream-line-equal _test-output-stream "39/compare *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-mem-with-reg")
+7457 (check-next-stream-line-equal _test-output-stream "39/compare-> *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-mem-with-reg")
7458
7459 89/<- %esp 5/r32/ebp
7460 5d/pop-to-ebp
@@ -7425,7 +7425,7 @@ if ('onhashchange' in window) {
7508 (flush _test-output-buffered-file)
7509 +-- 6 lines: #? # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
7515
-7516 (check-next-stream-line-equal _test-output-stream "3b/compare *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-reg-with-mem")
+7516 (check-next-stream-line-equal _test-output-stream "3b/compare<- *(ebp+0x00000008) 0x00000000/r32" "F - test-compare-reg-with-mem")
7517
7518 89/<- %esp 5/r32/ebp
7519 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
similarity index 100%
rename from opcodes
rename to 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()
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