https://github.com/akkartik/mu/blob/master/subx/apps/pack.subx
  1 # Read a text file of SubX instructions from stdin, and convert it into a list
  2 # of whitespace-separated ascii hex bytes on stdout, suitable to be further
  3 # processed by apps/hex.
  4 #
  5 # To run (from the subx/ directory):
  6 #   $ ./subx translate *.subx apps/pack.subx -o apps/pack
  7 #   $ echo '05/add-to-EAX 0x20/imm32'  |./subx run apps/pack
  8 # Expected output:
  9 #   05 20 00 00 00  # 05/add-to-EAX 0x20/imm32
 10 # The original instruction gets included as a comment at the end of each
 11 # converted line.
 12 #
 13 # There's zero error-checking. For now we assume the input program is valid.
 14 # We'll continue to rely on the C++ version for error messages.
 15 #
 16 # Label definitions and uses are left untouched for a future 'pass'.
 17 
 18 == code
 19 #   instruction                     effective address                                                   register    displacement    immediate
 20 # . op          subop               mod             rm32          base        index         scale       r32
 21 # . 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
 22 
 23     # for debugging: run a single test
 24 #?     e8/call test-emit-hex-zero-pad/disp32
 25 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 26 #?     eb/jump  $main:end/disp8
 27 
 28 # main: run tests if necessary, convert stdin if not
 29     # . prolog
 30     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 31     # - if argc > 1 and argv[1] == "test" then return run_tests()
 32     # . argc > 1
 33     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
 34     7e/jump-if-lesser-or-equal  $run-main/disp8
 35     # . argv[1] == "test"
 36     # . . push args
 37     68/push  "test"/imm32
 38     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 39     # . . call
 40     e8/call  kernel-string-equal/disp32
 41     # . . discard args
 42     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 43     # . check result
 44     3d/compare-EAX  1/imm32
 45     75/jump-if-not-equal  $run-main/disp8
 46     # . run-tests()
 47     e8/call  run-tests/disp32
 48     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 49     eb/jump  $main:end/disp8
 50 $run-main:
 51     # - otherwise convert stdin
 52     # var ed/EAX : exit-descriptor
 53     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
 54     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
 55     # configure ed to really exit()
 56     # . ed->target = 0
 57     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
 58     # return convert(Stdin, 1/stdout, 2/stderr, ed)
 59     # . . push args
 60     50/push-EAX/ed
 61     68/push  Stderr/imm32
 62     68/push  Stdout/imm32
 63     68/push  Stdin/imm32
 64     # . . call
 65     e8/call  convert/disp32
 66     # . . discard args
 67     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
 68     # . syscall(exit, 0)
 69     bb/copy-to-EBX  0/imm32
 70 $main:end:
 71     b8/copy-to-EAX  1/imm32/exit
 72     cd/syscall  0x80/imm8
 73 
 74 # - big picture
 75 # We'll operate on each line/instruction in isolation. That way we only need to
 76 # allocate memory for converting a single instruction.
 77 #
 78 # To pack an entire file:
 79 #   skip segment headers
 80 #   pack every instruction in the code segment
 81 #   skip other segments
 82 
 83 # - To pack an instruction, following the C++ version:
 84 # read line
 85 # parse words
 86 # read first word as opcode and write-slice
 87 # if 0f or f2 or f3 read second opcode and write-slice
 88 # if 'f2 0f' or 'f3 0f' read third opcode and write-slice
 89 # scan words
 90 #   if has metadata 'mod', parse into mod
 91 #   if has metadata 'rm32', parse into rm32
 92 #   if has metadata 'r32', parse into r32
 93 #   if has metadata 'subop', parse into r32
 94 # if at least one of the 3 was present, print-byte
 95 # scan words
 96 #   if has metadata 'base', parse into base
 97 #   if has metadata 'index', parse into index
 98 #   if has metadata 'scale', parse into scale
 99 # if at least one of the 3 was present, print-byte
100 # parse errors => <abort>
101 # scan words
102 #   if has metadata 'disp8', emit as 1 byte
103 #   if has metadata 'disp16', emit as 2 bytes
104 #   if has metadata 'disp32', emit as 4 bytes
105 # scan words
106 #   if has metadata 'imm8', emit
107 #   if has metadata 'imm32', emit as 4 bytes
108 # finally, emit line prefixed with a '  # '
109 
110 # simplifications since we perform zero error handling (continuing to rely on the C++ version for that):
111 #   missing fields are always 0-filled
112 #   bytes never mentioned are silently dropped; if you don't provide /mod, /rm32 or /r32 you don't get a 0 modrm byte. You get *no* modrm byte.
113 #   in case of conflict, last operand with a name is recognized
114 #   silently drop extraneous operands
115 #   unceremoniously abort on non-numeric operands except disp or imm
116 
117 # primary state: line
118 #   stream of 512 bytes; abort if it ever overflows
119 #
120 # conceptual hierarchy within a line:
121 #   line = words separated by ' ', maybe followed by comment starting with '#'
122 #   word = name until '/', then 0 or more metadata separated by '/'
123 #
124 # we won't bother saving the internal structure of lines; reparsing should be cheap using two primitives:
125 #   next-token(stream, delim char) -> slice (start, end pointers)
126 #   slice-equal?(slice, kernel string)
127 
128 # helpers:
129 #   emit(out : &buffered-file, word : &slice, width : int)
130 #     if slice is all hex digits, parse and print appropriate digits
131 #     otherwise just write-slice
132 #   has-metadata?(word : &slice, s : &kernel-string) -> bool
133 
134 convert:  # in : (address buffered-file), out : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> <void>
135     # pseudocode:
136     #   line = new-stream(512, 1)
137     #   repeatedly
138     #     clear-stream(line)
139     #     EAX = read-line(in, line, err, ed)
140     #     if EAX == EOF break
141     #     convert-instruction(line, out, err, ed)
142     #   flush(out)
143     #
144     # . prolog
145     55/push-EBP
146     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
147     # . save registers
148     # . restore registers
149     # . epilog
150     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
151     5d/pop-to-EBP
152     c3/return
153 
154 # (re)compute the bounds of the next word in the line
155 next-word:  # line : (address stream byte), out : (address slice)
156     # . prolog
157     55/push-EBP
158     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
159     # . save registers
160     50/push-EAX
161     51/push-ECX
162     56/push-ESI
163     57/push-EDI
164     # ESI = line
165     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
166     # EDI = out
167     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
168     # skip-chars-matching(line, ' ')
169     # . . push args
170     68/push  0x20/imm32/space
171     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
172     # . . call
173     e8/call  skip-chars-matching/disp32
174     # . . discard args
175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
176     # out->start = &line->data[line->read]
177     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
178     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
179     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
180     # if line->data[line->read] == '#': out->end = &line->data[line->write]), skip rest of stream and return
181     # . EAX = line->data[line->read]
182     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
183     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
184     # . compare
185     3d/compare-EAX-with  0x23/imm32/pound
186     75/jump-if-not-equal  $next-word:not-comment/disp8
187     # . out->end = &line->data[line->write]
188     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
189     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  0/index/EAX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+EAX+12 to EAX
190     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
191     # . line->read = line->write
192     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
193     # . return
194     eb/jump  $next-word:end/disp8
195 $next-word:not-comment:
196     # otherwise skip-chars-not-matching(line, ' ')
197     # . . push args
198     68/push  0x20/imm32/space
199     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
200     # . . call
201     e8/call  skip-chars-not-matching/disp32
202     # . . discard args
203     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
204     # out->end = &line->data[line->read]
205     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
206     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
207     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
208 $next-word:end:
209     # . restore registers
210     5f/pop-to-EDI
211     5e/pop-to-ESI
212     59/pop-to-ECX
213     58/pop-to-EAX
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-next-word:
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-stream)
225     # . . push args
226     68/push  _test-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     # var slice/ECX = {0, 0}
232     68/push  0/imm32/end
233     68/push  0/imm32/start
234     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
235     # write(_test-stream, "  ab")
236     # . . push args
237     68/push  "  ab"/imm32
238     68/push  _test-stream/imm32
239     # . . call
240     e8/call  write/disp32
241     # . . discard args
242     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
243     # next-word(_test-stream, slice)
244     # . . push args
245     51/push-ECX
246     68/push  _test-stream/imm32
247     # . . call
248     e8/call  next-word/disp32
249     # . . discard args
250     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
251     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
252     # . check-ints-equal(slice->start - _test-stream, 14, msg)
253     # . . push args
254     68/push  "F - test-next-word: start"/imm32
255     68/push  0xe/imm32
256     # . . push slice->start - _test-stream
257     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
258     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
259     50/push-EAX
260     # . . call
261     e8/call  check-ints-equal/disp32
262     # . . discard args
263     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
264     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
265     # . check-ints-equal(slice->end - _test-stream, 16, msg)
266     # . . push args
267     68/push  "F - test-next-word: end"/imm32
268     68/push  0x10/imm32
269     # . . push slice->end - _test-stream
270     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
271     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
272     50/push-EAX
273     # . . call
274     e8/call  check-ints-equal/disp32
275     # . . discard args
276     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
277     # . epilog
278     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
279     5d/pop-to-EBP
280     c3/return
281 
282 test-next-word-returns-whole-comment:
283     # . prolog
284     55/push-EBP
285     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
286     # setup
287     # . clear-stream(_test-stream)
288     # . . push args
289     68/push  _test-stream/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     # var slice/ECX = {0, 0}
295     68/push  0/imm32/end
296     68/push  0/imm32/start
297     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
298     # write(_test-stream, "  # a")
299     # . . push args
300     68/push  "  # a"/imm32
301     68/push  _test-stream/imm32
302     # . . call
303     e8/call  write/disp32
304     # . . discard args
305     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
306     # next-word(_test-stream, slice)
307     # . . push args
308     51/push-ECX
309     68/push  _test-stream/imm32
310     # . . call
311     e8/call  next-word/disp32
312     # . . discard args
313     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
314     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
315     # . check-ints-equal(slice->start - _test-stream, 14, msg)
316     # . . push args
317     68/push  "F - test-next-word-returns-whole-comment: start"/imm32
318     68/push  0xe/imm32
319     # . . push slice->start - _test-stream
320     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
321     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
322     50/push-EAX
323     # . . call
324     e8/call  check-ints-equal/disp32
325     # . . discard args
326     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
327     # check-ints-equal(slice->end - _test-stream->data, 5, msg)
328     # . check-ints-equal(slice->end - _test-stream, 17, msg)
329     # . . push args
330     68/push  "F - test-next-word-returns-whole-comment: end"/imm32
331     68/push  0x11/imm32
332     # . . push slice->end - _test-stream
333     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
334     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
335     50/push-EAX
336     # . . call
337     e8/call  check-ints-equal/disp32
338     # . . discard args
339     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
340     # . epilog
341     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
342     5d/pop-to-EBP
343     c3/return
344 
345 # print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte
346 emit-hex:  # out : (address buffered-file), n : int, width : int
347     # . prolog
348     55/push-EBP
349     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
350     # . save registers
351     50/push-EAX
352     51/push-ECX
353     52/push-EDX
354     53/push-EBX
355     57/push-EDI
356     # EDI = out
357     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
358     # EBX = n
359     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy *(EBP+12) to EBX
360     # EDX = width
361     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8      .                 # copy *(EBP+16) to EDX
362     # var curr/ECX = 0
363     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
364 $emit-hex:loop:
365     # if (curr >= width) break
366     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX and EDX
367     7d/jump-if-greater-or-equal  $emit-hex:end/disp8
368     # print-byte(out, EBX)
369     # . . push args
370     53/push-EBX
371     57/push-EDI
372     # . . call
373     e8/call  print-byte/disp32
374     # . . discard args
375     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
376     # write-byte(out, ' ')
377     # . . push args
378     68/push  0x20/imm32/space
379     57/push-EDI
380     # . . call
381     e8/call  write-byte/disp32
382     # . . discard args
383     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
384     # EBX = EBX >> 8
385     c1/shift    5/subop/logic-right 3/mod/direct    3/rm32/EBX    .           .             .           .           .               8/imm8            # shift EBX right by 8 bits, while padding zeroes
386     # ++curr
387     41/increment-ECX
388     eb/jump  $emit-hex:loop/disp8
389 $emit-hex:end:
390     # . restore registers
391     5f/pop-to-EDI
392     5b/pop-to-EBX
393     5a/pop-to-EAX
394     59/pop-to-ECX
395     58/pop-to-EAX
396     # . epilog
397     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
398     5d/pop-to-EBP
399     c3/return
400 
401 test-emit-hex-single-byte:
402     # setup
403     # . clear-stream(_test-stream)
404     # . . push args
405     68/push  _test-stream/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     # . clear-stream(_test-buffered-file+4)
411     # . . push args
412     b8/copy-to-EAX  _test-buffered-file/imm32
413     05/add-to-EAX  4/imm32
414     50/push-EAX
415     # . . call
416     e8/call  clear-stream/disp32
417     # . . discard args
418     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
419     # emit-hex(_test-buffered-file, 0xab, 1)
420     # . . push args
421     68/push  1/imm32
422     68/push  0xab/imm32
423     68/push  _test-buffered-file/imm32
424     # . . call
425     e8/call  emit-hex/disp32
426     # . . discard args
427     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
428     # flush(_test-buffered-file)
429     # . . push args
430     68/push  _test-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     # check-ints-equal(*_test-stream->data, 'ab ', msg)
436     # . . push args
437     68/push  "F - test-emit-hex-single-byte"/imm32
438     68/push  0x206261/imm32
439     # . . push *_test-stream->data
440     b8/copy-to-EAX  _test-stream/imm32
441     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
442     # . . call
443     e8/call  check-ints-equal/disp32
444     # . . discard args
445     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
446     # . end
447     c3/return
448 
449 test-emit-hex-multiple-byte:
450     # setup
451     # . clear-stream(_test-stream)
452     # . . push args
453     68/push  _test-stream/imm32
454     # . . call
455     e8/call  clear-stream/disp32
456     # . . discard args
457     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
458     # . clear-stream(_test-buffered-file+4)
459     # . . push args
460     b8/copy-to-EAX  _test-buffered-file/imm32
461     05/add-to-EAX  4/imm32
462     50/push-EAX
463     # . . call
464     e8/call  clear-stream/disp32
465     # . . discard args
466     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
467     # emit-hex(_test-buffered-file, 0x1234, 2)
468     # . . push args
469     68/push  2/imm32
470     68/push  0x1234/imm32
471     68/push  _test-buffered-file/imm32
472     # . . call
473     e8/call  emit-hex/disp32
474     # . . discard args
475     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
476     # flush(_test-buffered-file)
477     # . . push args
478     68/push  _test-buffered-file/imm32
479     # . . call
480     e8/call  flush/disp32
481     # . . discard args
482     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
483     # check(_test-stream->data == '34 12 ')
484     # . check-ints-equal(_test-stream->data[0..3], '34 1', msg)
485     # . . push args
486     68/push  "F - test-emit-hex-multiple-byte/1"/imm32
487     68/push  0x31203433/imm32
488     # . . push *_test-stream->data
489     b8/copy-to-EAX  _test-stream/imm32
490     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
491     # . . call
492     e8/call  check-ints-equal/disp32
493     # . . discard args
494     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
495     # . check-ints-equal(_test-stream->data[4..7], '2 ', msg)
496     # . . push args
497     68/push  "F - test-emit-hex-multiple-byte/2"/imm32
498     68/push  0x2032/imm32
499     # . . push *_test-stream->data
500     b8/copy-to-EAX  _test-stream/imm32
501     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0x10/disp8      .                 # push *(EAX+16)
502     # . . call
503     e8/call  check-ints-equal/disp32
504     # . . discard args
505     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
506     # . end
507     c3/return
508     # . end
509     c3/return
510 
511 test-emit-hex-zero-pad:
512     # setup
513     # . clear-stream(_test-stream)
514     # . . push args
515     68/push  _test-stream/imm32
516     # . . call
517     e8/call  clear-stream/disp32
518     # . . discard args
519     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
520     # . clear-stream(_test-buffered-file+4)
521     # . . push args
522     b8/copy-to-EAX  _test-buffered-file/imm32
523     05/add-to-EAX  4/imm32
524     50/push-EAX
525     # . . call
526     e8/call  clear-stream/disp32
527     # . . discard args
528     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
529     # emit-hex(_test-buffered-file, 0xab, 2)
530     # . . push args
531     68/push  2/imm32
532     68/push  0xab/imm32
533     68/push  _test-buffered-file/imm32
534     # . . call
535     e8/call  emit-hex/disp32
536     # . . discard args
537     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
538     # flush(_test-buffered-file)
539     # . . push args
540     68/push  _test-buffered-file/imm32
541     # . . call
542     e8/call  flush/disp32
543     # . . discard args
544     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
545     # check(_test-stream->data == '00 ab')
546     # . check-ints-equal(*_test-stream->data, 'ab 0', msg)
547     # . . push args
548     68/push  "F - test-emit-hex-zero-pad/1"/imm32
549     68/push  0x30206261/imm32
550     # . . push *_test-stream->data
551     b8/copy-to-EAX  _test-stream/imm32
552     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
553     # . . call
554     e8/call  check-ints-equal/disp32
555     # . . discard args
556     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
557     # . check-ints-equal(*_test-stream->data[1], '0 ', msg)
558     # . . push args
559     68/push  "F - test-emit-hex-zero-pad/2"/imm32
560     68/push  0x2030/imm32
561     # . . push *_test-stream->data
562     b8/copy-to-EAX  _test-stream/imm32
563     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0x10/disp8      .                 # push *(EAX+16)
564     # . . call
565     e8/call  check-ints-equal/disp32
566     # . . discard args
567     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
568     # . end
569     c3/return
570 
571 # . . vim:nowrap:textwidth=0