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 # compare var1/reg var2 => 3b/compare-> var2/rm32 var1/r32 +5290 # compare var1/reg var2 => 3b/compare<- var2/rm32 var1/r32 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 # check output -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 # . epilogue 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 # check output -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 # . epilogue 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