about summary refs log tree commit diff stats
path: root/065write-buffered.subx
blob: a0161d45248e835ac83f3148c2adf2f8cb4e2a8a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# write-buffered: like 'write', but for a buffered-file

== code
#   instruction                     effective address                                                   register    displacement    immediate
# . op          subop               mod             rm32          base        index         scale       r32
# . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes

write-buffered:  # f : (address buffered-file), msg : (address array byte) -> <void>
    # pseudocode:
    #   in = msg->data
    #   inend = &msg->data[msg->length]
    #   while (in < inend)
    #     if f->write >= f->length
    #       flush(f)
    #       clear-stream(f)
    #     c = *in
    #     f->data[f->write] = c
    #     ++f->write
    #     ++in
    #
    # registers:
    #   in: esi
    #   inend: ecx
    #   f: edi
    #   f->length: edx
    #   f->write: ebx (cached; need to keep in sync)
    #   c: eax
    #
    # . prolog
    55/push-ebp
    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    56/push-esi
    57/push-edi
    # eax = msg
    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
    # in/esi = msg->data
    8d/copy-address                 1/mod/*+disp8   0/rm32/eax    .           .             .           6/r32/esi   4/disp8         .                 # copy eax+4 to esi
    # inend/ecx = &msg->data[msg->length]
    8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    8d/copy-address                 0/mod/indirect  4/rm32/sib    6/base/esi  1/index/ecx   .           1/r32/ecx   .               .                 # copy esi+ecx to ecx
    # edi = f
    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
    # edx = f->length
    8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           2/r32/edx   0xc/disp8       .                 # copy *(edi+12) to edx
    # ebx = f->write
    8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           3/r32/ebx   4/disp8         .                 # copy *(edi+4) to ebx
$write-buffered:loop:
    # if (in >= inend) break
    39/compare                      3/mod/direct    6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare esi with ecx
    73/jump-if-greater-or-equal-unsigned  $write-buffered:loop-end/disp8
    # if (f->write >= f->length) flush and clear f's stream
    39/compare                      3/mod/direct    3/rm32/ebx    .           .             .           2/r32/edx   .               .                 # compare ebx with edx
    7c/jump-if-lesser  $write-buffered:to-stream/disp8
    # . persist f->write
    89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           3/r32/ebx   4/disp8         .                 # copy ebx to *(edi+4)
    # . flush(f)
    # . . push args
    57/push-edi
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # . clear-stream(stream = f+4)
    # . . push args
    8d/copy-address                 1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy edi+4 to eax
    50/push-eax
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # . f->write must now be 0; update its cache at ebx
    31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
$write-buffered:to-stream:
    # write to stream
    # f->data[f->write] = *in
    # . AL = *in
    31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
    # . f->data[f->write] = AL
    88/copy-byte                    1/mod/*+disp8   4/rm32/sib    7/base/edi  3/index/ebx   .           0/r32/AL    0x10/disp8      .                 # copy AL to *(edi+ebx+16)
    # ++f->write
    43/increment-ebx
    # ++in
    46/increment-esi
    eb/jump  $write-buffered:loop/disp8
$write-buffered:loop-end:
    # persist necessary variables from registers
    89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           3/r32/ebx   4/disp8         .                 # copy ebx to *(edi+4)
$write-buffered:end:
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilog
    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5d/pop-to-ebp
    c3/return

test-write-buffered:
    # - check that write-buffered writes to the file
    # setup
    # . clear-stream(_test-stream)
    # . . push args
    68/push  _test-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # . clear-stream(_test-buffered-file+4)
    # . . push args
    b8/copy-to-eax  _test-buffered-file/imm32
    05/add-to-eax  4/imm32
    50/push-eax
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # write-buffered(_test-buffered-file, "Abc")
    # . . push args
    68/push  "Abc"/imm32
    68/push  _test-buffered-file/imm32
    # . . call
    e8/call  write-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # flush(_test-buffered-file)
    # . . push args
    68/push  _test-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # check-stream-equal(_test-stream, "Abc", msg)
    # . . push args
    68/push  "F - test-write-buffered-single"/imm32
    68/push  "Abc"/imm32
    68/push  _test-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # . end
    c3/return

test-write-buffered-with-intermediate-flush:
    # - check that write-buffered flushes in the middle if its buffer fills up
    # setup
    # . clear-stream(_test-stream)
    # . . push args
    68/push  _test-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # . clear-stream(_test-buffered-file+4)
    # . . push args
    b8/copy-to-eax  _test-buffered-file/imm32
    05/add-to-eax  4/imm32
    50/push-eax
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # _test-stream can hold 8 bytes, but _test-buffered-file can hold only 6.
    # Try to write 7 bytes.
    # . write-buffered(_test-buffered-file, "Abcdefg")
    # . . push args
    68/push  "Abcdefg"/imm32
    68/push  _test-buffered-file/imm32
    # . . call
    e8/call  write-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # don't flush
    # 6 bytes should still have gotten to _test-stream
    # . check-ints-equal(*_test-stream->write, 6, msg)
    # . . push args
    68/push  "F - test-write-buffered-with-intermediate-flush: flushed data"/imm32
    68/push  6/imm32
    # . . push *_test-stream->write
    b8/copy-to-eax  _test-stream/imm32
    ff          6/subop/push        0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # push *eax
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # and 1 byte should still be in _test-buffered-file
    # . check-ints-equal(*_test-buffered-file->write, 1, msg)
    # . . push args
    68/push  "F - test-write-buffered-with-intermediate-flush: unflushed bytes"/imm32
    68/push  1/imm32
    # . . push *_test-buffered-file->write
    b8/copy-to-eax  _test-buffered-file/imm32
    ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # . end
    c3/return

== data

# The buffered file for standard error.
Stderr:
    # file descriptor or (address stream)
    2/imm32  # standard error
    # current write index
    0/imm32
    # current read index
    0/imm32
    # length
    8/imm32
    # data
    00 00 00 00 00 00 00 00  # 8 bytes

# TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But
# I don't want to type in 1024 bytes here.

# . . vim:nowrap:textwidth=0
131556325039f02f864bd8ae7683c'>^
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
































































                                                                                                                                                                      
                                                                                                                                                







                                                                                                                                                                      
                                                                                                                                                


                                                                                                                                                                                        
                                                                                                                                                                                        



                                                                                                                                                                                                             
                                                                                                                                                                                                             


                                                                                                                                                                                        
                                                                                                                                                                                        



                                                                                                                                                                                                             
                                                                                                                                                                                                             


                                                                                                                                                                                        
                                                                                                                                                                                        



                                                                                                                                                                                                             
                                                                                                                                                                                                             


                                                                                                                                                                                        
                                                                                                                                                                                        



                                                                                                                                                                                                             
                                                                                                                                                                                                             


                                                                                                                                                                                        
                                                                                                                                                                                        



                                                                                                                                                                                                             
                                                                                                                                                                                                             









                                                                                                                                                                                                             
                                                                                                                                                                                        

                                                                                                                                                                                                             
                                                                                                                                                                                                             

                                                                                                                                                                                       
                                                                                                                                                                                       
                                                                                                                                                                                       
                                                                                                                                                                                       

                                                                                                                                                                                                             
                                                                                                                                                                                                             

                                                                                                                                                                                        
                                                                                                                                                                                        

                                                                             
                                                                             




                                                                                                                                                                     





                                                                                                                                                                     







                                                                                                                                                                     









                                                                                                                                                                     





                                                                                                                                                                    





                                                                                                                                                                    








                                                                                                                                                                    








                                                                                                                                                                    









                                                                                                                                                                                                             







                                                                                                                                                                                        
               
## 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 ")"}
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<-",              .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<-",              .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/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]}
var/reg <- sub *var2/reg2   {.name="sub",             .inouts=[reg2], .outputs=[reg],   .subx-name="2b/sub->",              .rm32="*" inouts[0],                        .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]}
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<-",              .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<-",              .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<-",               .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<-",               .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<-",              .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<-",              .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/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]}
var1/reg <- copy *var2/reg2 {.name="copy",            .inouts=[reg2], .outputs=[reg],   .subx-name="8b/copy-from",          .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"}

Array operations

var/reg <- length var2/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] "<<2)",  .r32=outputs[0]}
var/reg <- index arr/rega: (addr array T), n
compare var, n              {.name="compare",         .inouts=[var, n],                 .subx-name="81 7/subop/compare",    .rm32="*(ebp+" inouts[0].stack-offset ")",                      .imm32=inouts[1]}
                            {.name="index",           .inouts=[rega, n], .outputs=[reg], .subx-name="8d/copy-address",      .rm32="*(" inouts[0] "+" inouts[1] "<<2)",  .r32=outputs[0]}

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-addr",          .rm32="*(" inouts[0] "+" offset(f) ")",     .r32=outputs[0]}

vim:ft=c:nowrap