about summary refs log tree commit diff stats
path: root/mu_instructions
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2020-03-21 17:25:52 -0700
committerKartik Agaram <vc@akkartik.com>2020-03-21 17:26:31 -0700
commitfa805850240423ed3aa079270da26839a68c763e (patch)
treef675c100998bc47673fbdbea59d1abaac0e282ba /mu_instructions
parentdf609237c1f583544b814454efb4790a67f7fd68 (diff)
downloadmu-fa805850240423ed3aa079270da26839a68c763e.tar.gz
6159
Diffstat (limited to 'mu_instructions')
-rw-r--r--mu_instructions383
1 files changed, 165 insertions, 218 deletions
diff --git a/mu_instructions b/mu_instructions
index b5d0e450..4c62d4bd 100644
--- a/mu_instructions
+++ b/mu_instructions
@@ -1,243 +1,190 @@
 ## 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-to", .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-to" (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 ")"}
-increment *var/reg          {.name="increment",       .inouts=[reg],                    .subx-name="ff 0/subop/increment",  .rm32="*" inouts[0]}
-
-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 ")"}
-decrement *var/reg          {.name="decrement",       .inouts=[reg],                    .subx-name="ff 1/subop/decrement",  .rm32="*" inouts[0]}
-
-var1/reg1 <- add var2/reg2  {.name="add",             .inouts=[reg2], .outputs=[reg1],  .subx-name="01/add-to",             .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]}
-var/reg <- add *var2/reg2   {.name="add",             .inouts=[reg2], .outputs=[reg],   .subx-name="03/add",                .rm32="*" inouts[0],                        .r32=outputs[0]}
-add-to var1, var2/reg       {.name="add-to",          .inouts=[var1, var2],             .subx-name="01/add-to",             .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]}
-add-to *var/reg, n          {.name="add-to",          .inouts=[reg, n],                 .subx-name="81 0/subop/add",        .rm32="*" inouts[0],                                            .imm32=inouts[1]}
-
-var1/reg1 <- sub var2/reg2  {.name="sub",             .inouts=[reg2], .outputs=[reg1],  .subx-name="29/subtract-from",      .rm32=outputs[0],                           .r32=inouts[0]}
-var/reg <- sub var2         {.name="sub",             .inouts=[var2], .outputs=[reg],   .subx-name="2b/subtract",           .rm32="*(ebp+" inouts[0].stack-offset ")",  .r32=outputs[0]}
-var/reg <- sub *var2/reg2   {.name="sub",             .inouts=[reg2], .outputs=[reg],   .subx-name="2b/subtract",           .rm32="*" inouts[0],                        .r32=outputs[0]}
-sub-from var1, var2/reg     {.name="sub-from",        .inouts=[var1, var2],             .subx-name="29/subtract-from",      .rm32="*(ebp+" inouts[0].stack-offset ")",  .r32=inouts[1]}
-var/eax <- sub n            {.name="sub",             .inouts=[n],    .outputs=[eax],   .subx-name="2d/subtract-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]}
-sub-from *var/reg, n        {.name="sub-from",        .inouts=[reg, n],                 .subx-name="81 5/subop/subtract",   .rm32="*" inouts[0],                                            .imm32=inouts[1]}
-
-var1/reg1 <- and var2/reg2  {.name="and",             .inouts=[reg2], .outputs=[reg1],  .subx-name="21/and-with",           .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]}
-var/reg <- and *var2/reg2   {.name="and",             .inouts=[reg2], .outputs=[reg],   .subx-name="23/and",                .rm32="*" inouts[0],                        .r32=outputs[0]}
-and-with var1, var2/reg     {.name="and-with",        .inouts=[var1, reg],              .subx-name="21/and-with",           .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]}
-and-with *var/reg, n        {.name="and-with",        .inouts=[reg, n],                 .subx-name="81 4/subop/and",        .rm32="*" inouts[0],                                            .imm32=inouts[1]}
-
-var1/reg1 <- or var2/reg2   {.name="or",              .inouts=[reg2], .outputs=[reg1],  .subx-name="09/or-with",            .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]}
-var/reg <- or *var2/reg2    {.name="or",              .inouts=[reg2], .outputs=[reg],   .subx-name="0b/or",                 .rm32="*" inouts[0],                        .r32=outputs[0]}
-or-with var1, var2/reg      {.name="or-with",         .inouts=[var1, reg],              .subx-name="09/or-with",            .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]}
-or-with *var/reg, n         {.name="or-with",         .inouts=[reg, n],                 .subx-name="81 1/subop/or",         .rm32="*" inouts[0],                                            .imm32=inouts[1]}
-
-var1/reg1 <- xor var2/reg2  {.name="xor",             .inouts=[reg2], .outputs=[reg1],  .subx-name="31/xor-with",           .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]}
-var/reg <- xor *var2/reg2   {.name="xor",             .inouts=[reg2], .outputs=[reg],   .subx-name="33/xor",                .rm32="*" inouts[0],                        .r32=outputs[0]}
-xor-with var1, var2/reg     {.name="xor-with",        .inouts=[var1, reg],              .subx-name="31/xor-with",           .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]}
-xor-with *var/reg, n        {.name="xor-with",        .inouts=[reg, n],                 .subx-name="81 6/subop/xor",        .rm32="*" inouts[0],                                            .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/<-",                 .rm32=outputs[0],                           .r32=inouts[0]}
-copy-to var1, var2/reg      {.name="copy-to",         .inouts=[var1, var2],             .subx-name="89/<-",                 .rm32="*(ebp+" inouts[0].stack-offset ")",  .r32=inouts[1]}
-var/reg <- copy var2        {.name="copy",            .inouts=[var2], .outputs=[reg],   .subx-name="8b/->",                 .rm32="*(ebp+" inouts[0].stack-offset ")",  .r32=outputs[0]}
-var1/reg <- copy *var2/reg2 {.name="copy",            .inouts=[reg2], .outputs=[reg],   .subx-name="8b/->",                 .rm32="*" inouts[0],                        .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]}
-copy-to *var/reg, n         {.name="copy-to",         .inouts=[reg, n],                 .subx-name="c7 0/subop/copy",       .rm32="*" inouts[0],                                            .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/reg1, var/reg {.name="compare",         .inouts=[reg1, reg],              .subx-name="39/compare",            .rm32="*" inouts[0],                        .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/reg, *var2/reg2 {.name="compare",         .inouts=[reg, reg2],              .subx-name="3b/compare<-",          .rm32="*" inouts[1],                        .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]}
-compare *var/reg, n         {.name="compare",         .inouts=[reg, n],                 .subx-name="81 7/subop/compare",    .rm32="*" inouts[0],                                            .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]}
-var/reg <- multiply *var2/reg2 { .name="multiply",    .inouts=[reg2], .outputs=[reg],   .subx-name="0f af/multiply",        .rm32="*" inouts[0],                        .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 containing block to jump to
-the start or end of. (Conditional branches read different combinations of CPU
-flags.)
-
-break                       {.name="break",                                             .subx-name="e9/jump break/disp32"}
-break label                 {.name="break",           .inouts=[label],                  .subx-name="e9/jump",                             .disp32=inouts[0] ":break"}
-
-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"}
-
-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"}
-
-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"}
-
-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                        {.name="loop",                                              .subx-name="e9/jump loop/disp32"}
-loop label                  {.name="loop",            .inouts=[label],                  .subx-name="e9/jump",                             .disp32=inouts[0] ":loop"}
-
-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-<                   {.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"}
-
-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"}
+The following chart shows all the instruction forms supported by Mu, along
+with the instruction they're translated to. Variables of the form "var/reg"
+live in a register, and other variables are assumed to live on the stack at
+some 'stack-offset' from ebp.
+
+var/eax <- increment              => "40/increment-eax"
+var/ecx <- increment              => "41/increment-ecx"
+var/edx <- increment              => "42/increment-edx"
+var/ebx <- increment              => "43/increment-ebx"
+var/esi <- increment              => "46/increment-esi"
+var/edi <- increment              => "47/increment-edi"
+increment var                     => "ff 0/subop/increment *(ebp+" var.stack-offset ")"
+increment *var/reg                => "ff 0/subop/increment *" reg
+
+var/eax <- decrement              => "48/decrement-eax"
+var/ecx <- decrement              => "49/decrement-ecx"
+var/edx <- decrement              => "4a/decrement-edx"
+var/ebx <- decrement              => "4b/decrement-ebx"
+var/esi <- decrement              => "4e/decrement-esi"
+var/edi <- decrement              => "4f/decrement-edi"
+decrement var                     => "ff 1/subop/decrement *(ebp+" var.stack-offset ")"
+decrement *var/reg                => "ff 1/subop/decrement *" reg
+
+var/reg <- add var2/reg2          => "01/add-to %" reg " " reg2 "/r32"
+var/reg <- add var2               => "03/add *(ebp+" var2.stack-offset ") " reg "/r32"
+var/reg <- add *var2/reg2         => "03/add *" reg2 " " reg "/r32"
+add-to var1, var2/reg             => "01/add-to *(ebp+" var1.stack-offset ") " reg "/r32"
+var/eax <- add n                  => "05/add-to-eax " n "/imm32"
+var/reg <- add n                  => "81 0/subop/add %" reg " " n "/imm32"
+add-to var, n                     => "81 0/subop/add *(ebp+" var.stack-offset ") " n "/imm32"
+add-to *var/reg, n                => "81 0/subop/add *" reg " " n "/imm32"
+
+var/reg <- subtract var2/reg2     => "29/subtract-from %" reg " " reg2 "/r32"
+var/reg <- subtract var2          => "2b/subtract *(ebp+" var2.stack-offset ") " reg "/r32"
+var/reg <- subtract *var2/reg2    => "2b/subtract *" reg2 " " reg1 "/r32"
+subtract-from var1, var2/reg2     => "29/subtract-from *(ebp+" var1.stack-offset ") " reg2 "/r32"
+var/eax <- subtract n             => "2d/subtract-from-eax " n "/imm32"
+var/reg <- subtract n             => "81 5/subop/subtract %" reg " " n "/imm32"
+subtract-from var, n              => "81 5/subop/subtract *(ebp+" var.stack-offset ") " n "/imm32"
+subtract-from *var/reg, n         => "81 5/subop/subtract *" reg " " n "/imm32"
+
+var/reg <- and var2/reg2          => "21/and-with %" reg " " reg2 "/r32"
+var/reg <- and var2               => "23/and *(ebp+" var2.stack-offset " " reg "/r32"
+var/reg <- and *var2/reg2         => "23/and *" reg2 " " reg "/r32"
+and-with var1, var2/reg           => "21/and-with *(ebp+" var1.stack-offset ") " reg "/r32"
+var/eax <- and n                  => "25/and-with-eax " n "/imm32"
+var/reg <- and n                  => "81 4/subop/and %" reg " " n "/imm32"
+and-with var, n                   => "81 4/subop/and *(ebp+" var.stack-offset ") " n "/imm32"
+and-with *var/reg, n              => "81 4/subop/and *" reg " " n "/imm32"
+
+var/reg <- or var2/reg2           => "09/or-with %" reg " " reg2 "/r32"
+var/reg <- or var2                => "0b/or *(ebp+" var2.stack-offset ") " reg "/r32"
+var/reg <- or *var2/reg2          => "0b/or *" reg2 " " reg "/r32"
+or-with var1, var2/reg2           => "09/or-with *(ebp+" var1.stack-offset " " reg2 "/r32"
+var/eax <- or n                   => "0d/or-with-eax " n "/imm32"
+var/reg <- or n                   => "81 1/subop/or %" reg " " n "/imm32"
+or-with var, n                    => "81 1/subop/or *(ebp+" var.stack-offset ") " n "/imm32"
+or-with *var/reg, n               => "81 1/subop/or *" reg " " n "/imm32"
+
+var/reg <- xor var2/reg2          => "31/xor-with %" reg " " reg2 "/r32"
+var/reg <- xor var2               => "33/xor *(ebp+" var2.stack-offset ") " reg "/r32"
+var/reg <- xor *var2/reg2         => "33/xor *" reg2 " " reg "/r32"
+xor-with var1, var2/reg           => "31/xor-with *(ebp+" var1.stack-offset ") " reg "/r32"
+var/eax <- xor n                  => "35/xor-with-eax " n "/imm32"
+var/reg <- xor n                  => "81 6/subop/xor %" reg " " n "/imm32"
+xor-with var, n                   => "81 6/subop/xor *(ebp+" var.stack-offset ") " n "/imm32"
+xor-with *var/reg, n              => "81 6/subop/xor *" reg " " n "/imm32"
+
+var/eax <- copy n                 => "b8/copy-to-eax " n "/imm32"
+var/ecx <- copy n                 => "b9/copy-to-ecx " n "/imm32"
+var/edx <- copy n                 => "ba/copy-to-edx " n "/imm32"
+var/ebx <- copy n                 => "bb/copy-to-ebx " n "/imm32"
+var/esi <- copy n                 => "be/copy-to-esi " n "/imm32"
+var/edi <- copy n                 => "bf/copy-to-edi " n "/imm32"
+var/reg <- copy var2/reg2         => "89/<- %" reg " " reg2 "/r32"
+copy-to var1, var2/reg            => "89/<- *(ebp+" var1.stack-offset ") " reg "/r32"
+var/reg <- copy var2              => "8b/-> *(ebp+" var2.stack-offset ") " reg "/r32"
+var/reg <- copy *var2/reg2        => "8b/-> *" reg2 " " reg "/r32"
+var/reg <- copy n                 => "c7 0/subop/copy %" reg " " n "/imm32"
+copy-to var, n                    => "c7 0/subop/copy *(ebp+" var.stack-offset ") " n "/imm32"
+copy-to *var/reg, n               => "c7 0/subop/copy *" reg " " n "/imm32"
+
+compare var1, var2/reg2           => "39/compare *(ebp+" var1.stack-offset ") " reg2 "/r32"
+compare *var1/reg1, var2/reg2     => "39/compare *" reg1 " " reg2 "/r32"
+compare var1/reg1, var2           => "3b/compare<- *(ebp+" var2.stack-offset ") " reg1 "/r32"
+compare var/reg, *var2/reg2       => "3b/compare<- *" reg " " n "/imm32"
+compare var/eax, n                => "3d/compare-eax-with " n "/imm32"
+compare var, n                    => "81 7/subop/compare *(ebp+" var.stack-offset ") " n "/imm32"
+compare *var/reg, n               => "81 7/subop/compare *" reg " " n "/imm32"
+
+var/reg <- multiply var2          => "0f af/multiply *(ebp+" var2.stack-offset ") " reg "/r32"
+var/reg <- multiply *var2/reg2    => "0f af/multiply *" reg2 " " reg "/r32"
+
+break                             => "e9/jump break/disp32"
+break label                       => "e9/jump " label ":break/disp32"
+loop                              => "e9/jump loop/disp32"
+loop label                        => "e9/jump " label ":loop/disp32"
+
+break-if-=                        => "0f 84/jump-if-= break/disp32"
+break-if-= label                  => "0f 84/jump-if-= " label ":break/disp32"
+loop-if-=                         => "0f 84/jump-if-= loop/disp32"
+loop-if-= label                   => "0f 84/jump-if-= " label ":loop/disp32"
+
+break-if-!=                       => "0f 85/jump-if-!= break/disp32"
+break-if-!= label                 => "0f 85/jump-if-!= " label ":break/disp32"
+loop-if-!=                        => "0f 85/jump-if-!= loop/disp32"
+loop-if-!= label                  => "0f 85/jump-if-!= " label ":loop/disp32"
+
+break-if-<                        => "0f 8c/jump-if-< break/disp32"
+break-if-< label                  => "0f 8c/jump-if-< " label ":break/disp32"
+loop-if-<                         => "0f 8c/jump-if-< loop/disp32"
+loop-if-< label                   => "0f 8c/jump-if-< " label ":loop/disp32"
+
+break-if->                        => "0f 8f/jump-if-> break/disp32"
+break-if-> label                  => "0f 8f/jump-if-> " label ":break/disp32"
+loop-if->                         => "0f 8f/jump-if-> loop/disp32"
+loop-if-> label                   => "0f 8f/jump-if-> " label ":loop/disp32"
+
+break-if-<=                       => "0f 8e/jump-if-<= break/disp32"
+break-if-<= label                 => "0f 8e/jump-if-<= " label ":break/disp32"
+loop-if-<=                        => "0f 8e/jump-if-<= loop/disp32"
+loop-if-<= label                  => "0f 8e/jump-if-<= " label ":loop/disp32"
+
+break-if->=                       => "0f 8d/jump-if->= break/disp32"
+break-if->= label                 => "0f 8d/jump-if->= " label ":break/disp32"
+loop-if->=                        => "0f 8d/jump-if->= loop/disp32"
+loop-if->= label                  => "0f 8d/jump-if->= " label ":loop/disp32"
+
+break-if-addr<                    => "0f 82/jump-if-addr< break/disp32"
+break-if-addr< label              => "0f 82/jump-if-addr< " label ":break/disp32"
+loop-if-addr<                     => "0f 82/jump-if-addr< loop/disp32"
+loop-if-addr< label               => "0f 82/jump-if-addr< " label ":loop/disp32"
+
+break-if-addr>                    => "0f 87/jump-if-addr> break/disp32"
+break-if-addr> label              => "0f 87/jump-if-addr> " label ":break/disp32"
+loop-if-addr>                     => "0f 87/jump-if-addr> loop/disp32"
+loop-if-addr> label               => "0f 87/jump-if-addr> " label ":loop/disp32"
+
+break-if-addr<=                   => "0f 86/jump-if-addr<= break/disp32"
+break-if-addr<= label             => "0f 86/jump-if-addr<= " label ":break/disp32"
+loop-if-addr<=                    => "0f 86/jump-if-addr<= loop/disp32"
+loop-if-addr<= label              => "0f 86/jump-if-addr<= " label ":loop/disp32"
+
+break-if-addr>=                   => "0f 83/jump-if-addr>= break/disp32"
+break-if-addr>= label             => "0f 83/jump-if-addr>= " label ":break/disp32"
+loop-if-addr>=                    => "0f 83/jump-if-addr>= loop/disp32"
+loop-if-addr>= label              => "0f 83/jump-if-addr>= " label ":loop/disp32"
+
+In the following instructions types are provided for clarity even if they must
+be provided in an earlier 'var' declaration.
 
 Address operations
 
 var/reg: (addr T) <- address var: T
-                            {.name="address",         .inouts=[var],  .outputs=[reg],   .subx-name="8d/copy-address",       .rm32="*(ebp+" inouts[0].stack-offset ")",  .r32=outputs[0]}
+  => "8d/copy-address *(ebp+" var.stack-offset ") " reg "/r32"
 
 Array operations
 
 var/reg <- length arr/reg2: (addr array T)
-                            {.name="length",          .inouts=[reg2], .outputs=[reg1],  .subx-name="8b/copy-from",          .rm32="*" inouts[0],                        .r32=outputs[0]}
-var/reg <- index arr/rega: (addr array T), idx/regi: int
-                            {.name="index",           .inouts=[rega, regi], .outputs=[reg], .subx-name="8d/copy-address",   .rm32="*(" inouts[0] "+" inouts[1] "<<" log2(sizeof(T)) "+4)", .r32=outputs[0]}
+  => "8b/-> *" reg2 " " reg "/r32"
+var/reg <- index arr/rega: (addr array T), idx/regi: int  # if size(T) is 4 or 8
+  => "8d/copy-address *(" rega "+" regi "<<" log2(size(T)) "+4) " reg "/r32"
 var/reg <- index arr: (array T sz), idx/regi: int
-                            {.name="index",           .inouts=[arr, regi], .outputs=[reg], .subx-name="8d/copy-address",    .rm32="*(ebp+" inouts[1] "<<" log2(sizeof(T)) " + " inouts[0].stack-offset+4 ")",  .r32=outputs[0]}
+  => "8d/copy-address *(ebp+" regi "<<" log2(size(T)) "+" (arr.stack-offset + 4) ") " reg "/r32"
 var/reg <- index arr/rega: (addr array T), n
-                            {.name="index",           .inouts=[rega, n], .outputs=[reg], .subx-name="8d/copy-address",      .rm32="*(" inouts[0] "+" inouts[1]*size(T) ")",  .r32=outputs[0]}
+  => "8d/copy-address *(" rega "+" (n*size(T)+4) ") " reg "/r32"
 var/reg <- index arr: (array T sz), n
-                            {.name="index",           .inouts=[arr, n], .outputs=[reg], .subx-name="8d/copy-address",       .rm32="*(ebp+" inouts[0].stack-offset+4 + inouts[1]*size(T) ")",  .r32=outputs[0]}
+  => "8d/copy-address *(ebp+" (arr.stack-offset+4+n*size(T)) ") " reg "/r32"
 
 var/reg: (offset T) <- compute-offset arr: (addr array T), idx/regi: int  # arr can be in reg or mem
-                            {.name="compute-offset",  .inouts=[arr, regi],  .outputs=[reg], .subx-name="69/multiply",       .rm32=inouts[1],                            .r32=outputs[0],    .imm32=sizeof(T)}
+  => "69/multiply %" regi " " size(T) "/imm32 " reg "/r32"
 var/reg: (offset T) <- compute-offset arr: (addr array T), idx: int       # arr can be in reg or mem
-                            {.name="compute-offset",  .inouts=[arr, regi],  .outputs=[reg], .subx-name="69/multiply",       .rm32="*(ebp+" inouts[1].stack-offset ")",  .r32=outputs[0],    .imm32=sizeof(T)}
+  => "69/multiply *(ebp+" idx.stack-offset ") " size(T) "/imm32 " reg "/r32"
 var/reg <- index arr/rega: (addr array T), o/rego: offset
-                            {.name="index",           .inouts=[rega, rego], .outputs=[reg], .subx-name="8d/copy-address",   .rm32="*(" inouts[0] "+" inouts[1] "+" "4)", .r32=outputs[0]}
+  => "8d/copy-address *(" rega "+" rego "+4) " reg "/r32"
 
 User-defined types
 
 If a record (product) type T was defined to have elements a, b, c, ... of
 types T_a, T_b, T_c, ..., then accessing one of those elements f of type T_f:
 
-var/reg: (addr T_f) <- get var2/reg2: (addr F), f: field name => 8d/copy-addr *(reg2+offset(f))
-                            {.name="get",             .inouts=[reg2, f],                .subx-name="8d/copy-address",       .rm32="*(" inouts[0] "+" offset(f) ")",     .r32=outputs[0]}
-var/reg: (addr T_f) <- get var2: (addr F), f: field name
-                            {.name="get",             .inouts=[reg2, f],                .subx-name="8d/copy-address",       .rm32="*(ebp+" inouts[0].stack-offset "+" offset(f) ")",  .r32=outputs[0]}
+var/reg: (addr T_f) <- get var2/reg2: (addr F), f
+  => "8d/copy-address *(" reg2 "+" offset(f) ") " reg "/r32"
+var/reg: (addr T_f) <- get var2: (addr F), f
+  => "8d/copy-address *(ebp+" var2.stack-offset "+" offset(f) ") " reg "/r32"
 
-vim:ft=c:nowrap
+vim:ft=mu:nowrap:tw&