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