https://github.com/akkartik/mu/blob/master/079emit.subx
  1 == code
  2 #   instruction                     effective address                                                   register    displacement    immediate
  3 # . op          subop               mod             rm32          base        index         scale       r32
  4 # . 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
  5 
  6 # If datum of 'word' is not a valid name, it must be a hex int. Parse and print
  7 # it in 'width' bytes of hex, least significant first.
  8 # Otherwise just print the entire word including metadata.
  9 # Always print a trailing space.
 10 emit:  # out : (address buffered-file), word : (address slice), width : int -> <void>
 11     # . prolog
 12     55/push-ebp
 13     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 14     # . save registers
 15     50/push-eax
 16     56/push-esi
 17     57/push-edi
 18     # esi = word
 19     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
 20     # var name/edi : (address slice) = {0, 0}
 21     68/push  0/imm32/end
 22     68/push  0/imm32/start
 23     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
 24     # datum = next-token-from-slice(word->start, word->end, '/')
 25     # . . push args
 26     57/push-edi
 27     68/push  0x2f/imm32/slash
 28     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
 29     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
 30     # . . call
 31     e8/call  next-token-from-slice/disp32
 32     # . . discard args
 33     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 34     # if (is-valid-name?(datum)) write-slice-buffered(out, word) and return
 35     # . eax = is-valid-name?(name)
 36     # . . push args
 37     57/push-edi
 38     # . . call
 39     e8/call  is-valid-name?/disp32
 40     # . . discard args
 41     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 42     # . if (eax != 0)
 43     3d/compare-eax-and  0/imm32
 44     74/jump-if-equal  $emit:hex-int/disp8
 45 $emit:name:
 46     # . write-slice-buffered(out, word)
 47     # . . push args
 48     56/push-esi
 49     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 50     # . . call
 51     e8/call  write-slice-buffered/disp32
 52     # . . discard args
 53     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 54     # . write-buffered(out, " ")
 55     # . . push args
 56     68/push  Space/imm32
 57     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 58     # . . call
 59     e8/call  write-buffered/disp32
 60     # . . discard args
 61     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 62     # . return
 63     eb/jump  $emit:end/disp8
 64     # otherwise emit-hex(out, parse-hex-int(datum), width)
 65     #   (Weird shit can happen here if the datum of 'word' isn't either a valid
 66     #   name or a hex number, but we're only going to be passing in real legal
 67     #   programs. We just want to make sure that valid names aren't treated as
 68     #   (valid) hex numbers.)
 69 $emit:hex-int:
 70     # . value/eax = parse-hex-int(datum)
 71     # . . push args
 72     57/push-edi
 73     # . . call
 74     e8/call  parse-hex-int/disp32
 75     # . . discard args
 76     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 77     # . emit-hex(out, value, width)
 78     # . . push args
 79     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
 80     50/push-eax
 81     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 82     # . . call
 83     e8/call  emit-hex/disp32
 84     # . . discard args
 85     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 86 $emit:end:
 87     # . reclaim locals
 88     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 89     # . restore registers
 90     5f/pop-to-edi
 91     5e/pop-to-esi
 92     58/pop-to-eax
 93     # . epilog
 94     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 95     5d/pop-to-ebp
 96     c3/return
 97 
 98 test-emit-number:
 99     # . prolog
100     55/push-ebp
101     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
102     # setup
103     # . clear-stream(_test-output-stream)
104     # . . push args
105     68/push  _test-output-stream/imm32
106     # . . call
107     e8/call  clear-stream/disp32
108     # . . discard args
109     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
110     # . clear-stream(_test-output-buffered-file+4)
111     # . . push args
112     b8/copy-to-eax  _test-output-buffered-file/imm32
113     05/add-to-eax  4/imm32
114     50/push-eax
115     # . . call
116     e8/call  clear-stream/disp32
117     # . . discard args
118     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
119     # (eax..ecx) = "30"
120     b8/copy-to-eax  "30"/imm32
121     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
122     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
123     05/add-to-eax  4/imm32
124     # var slice/ecx = {eax, ecx}
125     51/push-ecx
126     50/push-eax
127     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
128     # emit(_test-output-buffered-file, slice, 1)
129     # . . push args
130     68/push  1/imm32
131     51/push-ecx
132     68/push  _test-output-buffered-file/imm32
133     # . . call
134     e8/call  emit/disp32
135     # . . discard args
136     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
137     # flush(_test-output-buffered-file)
138     # . . push args
139     68/push  _test-output-buffered-file/imm32
140     # . . call
141     e8/call  flush/disp32
142     # . . discard args
143     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
144     # check-stream-equal(_test-output-stream, "30 ", msg)
145     # . . push args
146     68/push  "F - test-emit-number/1"/imm32
147     68/push  "30 "/imm32
148     68/push  _test-output-stream/imm32
149     # . . call
150     e8/call  check-stream-equal/disp32
151     # . . discard args
152     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
153     # . epilog
154     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
155     5d/pop-to-ebp
156     c3/return
157 
158 test-emit-negative-number:
159     # test support for sign-extending negative numbers
160     # . prolog
161     55/push-ebp
162     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
163     # setup
164     # . clear-stream(_test-output-stream)
165     # . . push args
166     68/push  _test-output-stream/imm32
167     # . . call
168     e8/call  clear-stream/disp32
169     # . . discard args
170     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
171     # . clear-stream(_test-output-buffered-file+4)
172     # . . push args
173     b8/copy-to-eax  _test-output-buffered-file/imm32
174     05/add-to-eax  4/imm32
175     50/push-eax
176     # . . call
177     e8/call  clear-stream/disp32
178     # . . discard args
179     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
180     # (eax..ecx) = "-2"
181     b8/copy-to-eax  "-2"/imm32
182     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
183     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
184     05/add-to-eax  4/imm32
185     # var slice/ecx = {eax, ecx}
186     51/push-ecx
187     50/push-eax
188     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
189     # emit(_test-output-buffered-file, slice, 2)
190     # . . push args
191     68/push  2/imm32
192     51/push-ecx
193     68/push  _test-output-buffered-file/imm32
194     # . . call
195     e8/call  emit/disp32
196     # . . discard args
197     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
198     # flush(_test-output-buffered-file)
199     # . . push args
200     68/push  _test-output-buffered-file/imm32
201     # . . call
202     e8/call  flush/disp32
203     # . . discard args
204     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
205     # check-stream-equal(_test-output-stream, "fe ff ", msg)
206     # . . push args
207     68/push  "F - test-emit-number/1"/imm32
208     68/push  "fe ff "/imm32
209     68/push  _test-output-stream/imm32
210     # . . call
211     e8/call  check-stream-equal/disp32
212     # . . discard args
213     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
214     # . epilog
215     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
216     5d/pop-to-ebp
217     c3/return
218 
219 test-emit-number-with-metadata:
220     # . prolog
221     55/push-ebp
222     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
223     # setup
224     # . clear-stream(_test-output-stream)
225     # . . push args
226     68/push  _test-output-stream/imm32
227     # . . call
228     e8/call  clear-stream/disp32
229     # . . discard args
230     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
231     # . clear-stream(_test-output-buffered-file+4)
232     # . . push args
233     b8/copy-to-eax  _test-output-buffered-file/imm32
234     05/add-to-eax  4/imm32
235     50/push-eax
236     # . . call
237     e8/call  clear-stream/disp32
238     # . . discard args
239     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
240     # (eax..ecx) = "-2/foo"
241     b8/copy-to-eax  "-2/foo"/imm32
242     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
243     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
244     05/add-to-eax  4/imm32
245     # var slice/ecx = {eax, ecx}
246     51/push-ecx
247     50/push-eax
248     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
249     # emit(_test-output-buffered-file, slice, 2)
250     # . . push args
251     68/push  2/imm32
252     51/push-ecx
253     68/push  _test-output-buffered-file/imm32
254     # . . call
255     e8/call  emit/disp32
256     # . . discard args
257     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
258     # flush(_test-output-buffered-file)
259     # . . push args
260     68/push  _test-output-buffered-file/imm32
261     # . . call
262     e8/call  flush/disp32
263     # . . discard args
264     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
265     # the '/foo' will have no impact on the output
266     # check-stream-equal(_test-output-stream, "fe ff ", msg)
267     # . . push args
268     68/push  "F - test-emit-number-with-metadata"/imm32
269     68/push  "fe ff "/imm32
270     68/push  _test-output-stream/imm32
271     # . . call
272     e8/call  check-stream-equal/disp32
273     # . . discard args
274     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
275     # . epilog
276     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
277     5d/pop-to-ebp
278     c3/return
279 
280 test-emit-non-number:
281     # . prolog
282     55/push-ebp
283     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
284     # setup
285     # . clear-stream(_test-output-stream)
286     # . . push args
287     68/push  _test-output-stream/imm32
288     # . . call
289     e8/call  clear-stream/disp32
290     # . . discard args
291     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
292     # . clear-stream(_test-output-buffered-file+4)
293     # . . push args
294     b8/copy-to-eax  _test-output-buffered-file/imm32
295     05/add-to-eax  4/imm32
296     50/push-eax
297     # . . call
298     e8/call  clear-stream/disp32
299     # . . discard args
300     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
301     # (eax..ecx) = "xyz"
302     b8/copy-to-eax  "xyz"/imm32
303     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
304     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
305     05/add-to-eax  4/imm32
306     # var slice/ecx = {eax, ecx}
307     51/push-ecx
308     50/push-eax
309     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
310     # emit(_test-output-buffered-file, slice, 2)
311     # . . push args
312     68/push  2/imm32
313     51/push-ecx
314     68/push  _test-output-buffered-file/imm32
315     # . . call
316     e8/call  emit/disp32
317     # . . discard args
318     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
319     # flush(_test-output-buffered-file)
320     # . . push args
321     68/push  _test-output-buffered-file/imm32
322     # . . call
323     e8/call  flush/disp32
324     # . . discard args
325     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
326     # check-stream-equal(_test-output-stream, "xyz", msg)
327     # . . push args
328     68/push  "F - test-emit-non-number"/imm32
329     68/push  "xyz "/imm32
330     68/push  _test-output-stream/imm32
331     # . . call
332     e8/call  check-stream-equal/disp32
333     # . . discard args
334     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
335     # . epilog
336     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
337     5d/pop-to-ebp
338     c3/return
339 
340 test-emit-non-number-with-metadata:
341     # . prolog
342     55/push-ebp
343     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
344     # setup
345     # . clear-stream(_test-output-stream)
346     # . . push args
347     68/push  _test-output-stream/imm32
348     # . . call
349     e8/call  clear-stream/disp32
350     # . . discard args
351     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
352     # . clear-stream(_test-output-buffered-file+4)
353     # . . push args
354     b8/copy-to-eax  _test-output-buffered-file/imm32
355     05/add-to-eax  4/imm32
356     50/push-eax
357     # . . call
358     e8/call  clear-stream/disp32
359     # . . discard args
360     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
361     # (eax..ecx) = "xyz/"
362     b8/copy-to-eax  "xyz/"/imm32
363     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
364     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
365     05/add-to-eax  4/imm32
366     # var slice/ecx = {eax, ecx}
367     51/push-ecx
368     50/push-eax
369     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
370     # emit(_test-output-buffered-file, slice, 2)
371     # . . push args
372     68/push  2/imm32
373     51/push-ecx
374     68/push  _test-output-buffered-file/imm32
375     # . . call
376     e8/call  emit/disp32
377     # . . discard args
378     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
379     # flush(_test-output-buffered-file)
380     # . . push args
381     68/push  _test-output-buffered-file/imm32
382     # . . call
383     e8/call  flush/disp32
384     # . . discard args
385     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
386     # check-stream-equal(_test-output-stream, "xyz/", msg)
387     # . . push args
388     68/push  "F - test-emit-non-number-with-metadata"/imm32
389     68/push  "xyz/ "/imm32
390     68/push  _test-output-stream/imm32
391     # . . call
392     e8/call  check-stream-equal/disp32
393     # . . discard args
394     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
395     # . epilog
396     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
397     5d/pop-to-ebp
398     c3/return
399 
400 test-emit-non-number-with-all-hex-digits-and-metadata:
401     # . prolog
402     55/push-ebp
403     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
404     # setup
405     # . clear-stream(_test-output-stream)
406     # . . push args
407     68/push  _test-output-stream/imm32
408     # . . call
409     e8/call  clear-stream/disp32
410     # . . discard args
411     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
412     # . clear-stream(_test-output-buffered-file+4)
413     # . . push args
414     b8/copy-to-eax  _test-output-buffered-file/imm32
415     05/add-to-eax  4/imm32
416     50/push-eax
417     # . . call
418     e8/call  clear-stream/disp32
419     # . . discard args
420     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
421     # (eax..ecx) = "abcd/xyz"
422     b8/copy-to-eax  "abcd/xyz"/imm32
423     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
424     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
425     05/add-to-eax  4/imm32
426     # var slice/ecx = {eax, ecx}
427     51/push-ecx
428     50/push-eax
429     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
430     # emit(_test-output-buffered-file, slice, 2)
431     # . . push args
432     68/push  2/imm32
433     51/push-ecx
434     68/push  _test-output-buffered-file/imm32
435     # . . call
436     e8/call  emit/disp32
437     # . . discard args
438     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
439     # flush(_test-output-buffered-file)
440     # . . push args
441     68/push  _test-output-buffered-file/imm32
442     # . . call
443     e8/call  flush/disp32
444     # . . discard args
445     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
446 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
472     # check-stream-equal(_test-output-stream, "abcd/xyz")
473     # . . push args
474     68/push  "F - test-emit-non-number-with-all-hex-digits"/imm32
475     68/push  "abcd/xyz "/imm32
476     68/push  _test-output-stream/imm32
477     # . . call
478     e8/call  check-stream-equal/disp32
479     # . . discard args
480     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
481     # . epilog
482     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
483     5d/pop-to-ebp
484     c3/return