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
 11     # . prologue
 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 datum/edi : (ref slice)
 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 != false)
 43     3d/compare-eax-and  0/imm32/false
 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. `emit` is mostly used by ntranslate, which is
 67     #   currently designed to only receive legal SubX programs. We just want to
 68     #   make sure that valid names aren't treated as (valid) hex numbers.)
 69 $emit:hex-int:
 70     # . var value/eax : int = 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     # . epilogue
 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     # . prologue
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->buffer)
111     # . . push args
112     68/push  $_test-output-buffered-file->buffer/imm32
113     # . . call
114     e8/call  clear-stream/disp32
115     # . . discard args
116     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
117     # (eax..ecx) = "30"
118     b8/copy-to-eax  "30"/imm32
119     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
120     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
121     05/add-to-eax  4/imm32
122     # var slice/ecx : (ref slice) = {eax, ecx}
123     51/push-ecx
124     50/push-eax
125     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
126     # emit(_test-output-buffered-file, slice, 1)
127     # . . push args
128     68/push  1/imm32
129     51/push-ecx
130     68/push  _test-output-buffered-file/imm32
131     # . . call
132     e8/call  emit/disp32
133     # . . discard args
134     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
135     # flush(_test-output-buffered-file)
136     # . . push args
137     68/push  _test-output-buffered-file/imm32
138     # . . call
139     e8/call  flush/disp32
140     # . . discard args
141     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
142     # check-stream-equal(_test-output-stream, "30 ", msg)
143     # . . push args
144     68/push  "F - test-emit-number/1"/imm32
145     68/push  "30 "/imm32
146     68/push  _test-output-stream/imm32
147     # . . call
148     e8/call  check-stream-equal/disp32
149     # . . discard args
150     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
151     # . epilogue
152     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
153     5d/pop-to-ebp
154     c3/return
155 
156 test-emit-negative-number:
157     # test support for sign-extending negative numbers
158     # . prologue
159     55/push-ebp
160     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
161     # setup
162     # . clear-stream(_test-output-stream)
163     # . . push args
164     68/push  _test-output-stream/imm32
165     # . . call
166     e8/call  clear-stream/disp32
167     # . . discard args
168     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
169     # . clear-stream($_test-output-buffered-file->buffer)
170     # . . push args
171     68/push  $_test-output-buffered-file->buffer/imm32
172     # . . call
173     e8/call  clear-stream/disp32
174     # . . discard args
175     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
176     # (eax..ecx) = "-2"
177     b8/copy-to-eax  "-2"/imm32
178     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
179     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
180     05/add-to-eax  4/imm32
181     # var slice/ecx : (ref slice) = {eax, ecx}
182     51/push-ecx
183     50/push-eax
184     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
185     # emit(_test-output-buffered-file, slice, 2)
186     # . . push args
187     68/push  2/imm32
188     51/push-ecx
189     68/push  _test-output-buffered-file/imm32
190     # . . call
191     e8/call  emit/disp32
192     # . . discard args
193     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
194     # flush(_test-output-buffered-file)
195     # . . push args
196     68/push  _test-output-buffered-file/imm32
197     # . . call
198     e8/call  flush/disp32
199     # . . discard args
200     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
201     # check-stream-equal(_test-output-stream, "fe ff ", msg)
202     # . . push args
203     68/push  "F - test-emit-number/1"/imm32
204     68/push  "fe ff "/imm32
205     68/push  _test-output-stream/imm32
206     # . . call
207     e8/call  check-stream-equal/disp32
208     # . . discard args
209     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
210     # . epilogue
211     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
212     5d/pop-to-ebp
213     c3/return
214 
215 test-emit-number-with-metadata:
216     # . prologue
217     55/push-ebp
218     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
219     # setup
220     # . clear-stream(_test-output-stream)
221     # . . push args
222     68/push  _test-output-stream/imm32
223     # . . call
224     e8/call  clear-stream/disp32
225     # . . discard args
226     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
227     # . clear-stream($_test-output-buffered-file->buffer)
228     # . . push args
229     68/push  $_test-output-buffered-file->buffer/imm32
230     # . . call
231     e8/call  clear-stream/disp32
232     # . . discard args
233     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
234     # (eax..ecx) = "-2/foo"
235     b8/copy-to-eax  "-2/foo"/imm32
236     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
237     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
238     05/add-to-eax  4/imm32
239     # var slice/ecx : (ref slice) = {eax, ecx}
240     51/push-ecx
241     50/push-eax
242     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
243     # emit(_test-output-buffered-file, slice, 2)
244     # . . push args
245     68/push  2/imm32
246     51/push-ecx
247     68/push  _test-output-buffered-file/imm32
248     # . . call
249     e8/call  emit/disp32
250     # . . discard args
251     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
252     # flush(_test-output-buffered-file)
253     # . . push args
254     68/push  _test-output-buffered-file/imm32
255     # . . call
256     e8/call  flush/disp32
257     # . . discard args
258     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
259     # the '/foo' will have no impact on the output
260     # check-stream-equal(_test-output-stream, "fe ff ", msg)
261     # . . push args
262     68/push  "F - test-emit-number-with-metadata"/imm32
263     68/push  "fe ff "/imm32
264     68/push  _test-output-stream/imm32
265     # . . call
266     e8/call  check-stream-equal/disp32
267     # . . discard args
268     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
269     # . epilogue
270     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
271     5d/pop-to-ebp
272     c3/return
273 
274 test-emit-non-number:
275     # . prologue
276     55/push-ebp
277     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
278     # setup
279     # . clear-stream(_test-output-stream)
280     # . . push args
281     68/push  _test-output-stream/imm32
282     # . . call
283     e8/call  clear-stream/disp32
284     # . . discard args
285     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
286     # . clear-stream($_test-output-buffered-file->buffer)
287     # . . push args
288     68/push  $_test-output-buffered-file->buffer/imm32
289     # . . call
290     e8/call  clear-stream/disp32
291     # . . discard args
292     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
293     # (eax..ecx) = "xyz"
294     b8/copy-to-eax  "xyz"/imm32
295     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
296     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
297     05/add-to-eax  4/imm32
298     # var slice/ecx : (ref slice) = {eax, ecx}
299     51/push-ecx
300     50/push-eax
301     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
302     # emit(_test-output-buffered-file, slice, 2)
303     # . . push args
304     68/push  2/imm32
305     51/push-ecx
306     68/push  _test-output-buffered-file/imm32
307     # . . call
308     e8/call  emit/disp32
309     # . . discard args
310     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
311     # flush(_test-output-buffered-file)
312     # . . push args
313     68/push  _test-output-buffered-file/imm32
314     # . . call
315     e8/call  flush/disp32
316     # . . discard args
317     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
318     # check-stream-equal(_test-output-stream, "xyz", msg)
319     # . . push args
320     68/push  "F - test-emit-non-number"/imm32
321     68/push  "xyz "/imm32
322     68/push  _test-output-stream/imm32
323     # . . call
324     e8/call  check-stream-equal/disp32
325     # . . discard args
326     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
327     # . epilogue
328     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
329     5d/pop-to-ebp
330     c3/return
331 
332 test-emit-non-number-with-metadata:
333     # . prologue
334     55/push-ebp
335     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
336     # setup
337     # . clear-stream(_test-output-stream)
338     # . . push args
339     68/push  _test-output-stream/imm32
340     # . . call
341     e8/call  clear-stream/disp32
342     # . . discard args
343     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
344     # . clear-stream($_test-output-buffered-file->buffer)
345     # . . push args
346     68/push  $_test-output-buffered-file->buffer/imm32
347     # . . call
348     e8/call  clear-stream/disp32
349     # . . discard args
350     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
351     # (eax..ecx) = "xyz/"
352     b8/copy-to-eax  "xyz/"/imm32
353     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
354     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
355     05/add-to-eax  4/imm32
356     # var slice/ecx : (ref slice) = {eax, ecx}
357     51/push-ecx
358     50/push-eax
359     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
360     # emit(_test-output-buffered-file, slice, 2)
361     # . . push args
362     68/push  2/imm32
363     51/push-ecx
364     68/push  _test-output-buffered-file/imm32
365     # . . call
366     e8/call  emit/disp32
367     # . . discard args
368     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
369     # flush(_test-output-buffered-file)
370     # . . push args
371     68/push  _test-output-buffered-file/imm32
372     # . . call
373     e8/call  flush/disp32
374     # . . discard args
375     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
376     # check-stream-equal(_test-output-stream, "xyz/", msg)
377     # . . push args
378     68/push  "F - test-emit-non-number-with-metadata"/imm32
379     68/push  "xyz/ "/imm32
380     68/push  _test-output-stream/imm32
381     # . . call
382     e8/call  check-stream-equal/disp32
383     # . . discard args
384     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
385     # . epilogue
386     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
387     5d/pop-to-ebp
388     c3/return
389 
390 test-emit-non-number-with-all-hex-digits-and-metadata:
391     # . prologue
392     55/push-ebp
393     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
394     # setup
395     # . clear-stream(_test-output-stream)
396     # . . push args
397     68/push  _test-output-stream/imm32
398     # . . call
399     e8/call  clear-stream/disp32
400     # . . discard args
401     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
402     # . clear-stream($_test-output-buffered-file->buffer)
403     # . . push args
404     68/push  $_test-output-buffered-file->buffer/imm32
405     # . . call
406     e8/call  clear-stream/disp32
407     # . . discard args
408     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
409     # (eax..ecx) = "abcd/xyz"
410     b8/copy-to-eax  "abcd/xyz"/imm32
411     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
412     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
413     05/add-to-eax  4/imm32
414     # var slice/ecx : (ref slice) = {eax, ecx}
415     51/push-ecx
416     50/push-eax
417     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
418     # emit(_test-output-buffered-file, slice, 2)
419     # . . push args
420     68/push  2/imm32
421     51/push-ecx
422     68/push  _test-output-buffered-file/imm32
423     # . . call
424     e8/call  emit/disp32
425     # . . discard args
426     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
427     # flush(_test-output-buffered-file)
428     # . . push args
429     68/push  _test-output-buffered-file/imm32
430     # . . call
431     e8/call  flush/disp32
432     # . . discard args
433     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
434 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
460     # check-stream-equal(_test-output-stream, "abcd/xyz")
461     # . . push args
462     68/push  "F - test-emit-non-number-with-all-hex-digits"/imm32
463     68/push  "abcd/xyz "/imm32
464     68/push  _test-output-stream/imm32
465     # . . call
466     e8/call  check-stream-equal/disp32
467     # . . discard args
468     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
469     # . epilogue
470     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
471     5d/pop-to-ebp
472     c3/return