https://github.com/akkartik/mu/blob/main/linux/129emit-hex.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 # print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte
  7 emit-hex:  # out: (addr buffered-file), n: int, width: int
  8     # . prologue
  9     55/push-ebp
 10     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 11     # . save registers
 12     50/push-eax
 13     51/push-ecx
 14     52/push-edx
 15     53/push-ebx
 16     57/push-edi
 17     # edi = out
 18     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
 19     # ebx = n
 20     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           3/r32/ebx   0xc/disp8       .                 # copy *(ebp+12) to ebx
 21     # edx = width
 22     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0x10/disp8      .                 # copy *(ebp+16) to edx
 23     # var curr/ecx: int = 0
 24     31/xor                          3/mod/direct    1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # clear ecx
 25 $emit-hex:loop:
 26     # if (curr >= width) break
 27     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
 28     7d/jump-if->=  $emit-hex:end/disp8
 29     # write-byte-hex-buffered(out, ebx)  # only BL used
 30     # . . push args
 31     53/push-ebx
 32     57/push-edi
 33     # . . call
 34     e8/call  write-byte-hex-buffered/disp32
 35     # . . discard args
 36     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 37     # write-byte-buffered(out, ' ')
 38     # . . push args
 39     68/push  0x20/imm32/space
 40     57/push-edi
 41     # . . call
 42     e8/call  write-byte-buffered/disp32
 43     # . . discard args
 44     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 45     # ebx = ebx >> 8
 46     c1/shift    5/subop/logic-right 3/mod/direct    3/rm32/ebx    .           .             .           .           .               8/imm8            # shift ebx right by 8 bits, while padding zeroes
 47 $emit-hex:continue:
 48     # ++curr
 49     41/increment-ecx
 50     eb/jump  $emit-hex:loop/disp8
 51 $emit-hex:end:
 52     # . restore registers
 53     5f/pop-to-edi
 54     5b/pop-to-ebx
 55     5a/pop-to-edx
 56     59/pop-to-ecx
 57     58/pop-to-eax
 58     # . epilogue
 59     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 60     5d/pop-to-ebp
 61     c3/return
 62 
 63 test-emit-hex-single-byte:
 64     # setup
 65     # . clear-stream(_test-output-stream)
 66     # . . push args
 67     68/push  _test-output-stream/imm32
 68     # . . call
 69     e8/call  clear-stream/disp32
 70     # . . discard args
 71     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 72     # . clear-stream($_test-output-buffered-file->buffer)
 73     # . . push args
 74     68/push  $_test-output-buffered-file->buffer/imm32
 75     # . . call
 76     e8/call  clear-stream/disp32
 77     # . . discard args
 78     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 79     # emit-hex(_test-output-buffered-file, 0xab, 1)
 80     # . . push args
 81     68/push  1/imm32
 82     68/push  0xab/imm32
 83     68/push  _test-output-buffered-file/imm32
 84     # . . call
 85     e8/call  emit-hex/disp32
 86     # . . discard args
 87     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 88     # flush(_test-output-buffered-file)
 89     # . . push args
 90     68/push  _test-output-buffered-file/imm32
 91     # . . call
 92     e8/call  flush/disp32
 93     # . . discard args
 94     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 95     # check-ints-equal(*_test-output-stream->data, 'ab ', msg)
 96     # . . push args
 97     68/push  "F - test-emit-hex-single-byte"/imm32
 98     68/push  0x206261/imm32
 99     # . . push *_test-output-stream->data
100     b8/copy-to-eax  _test-output-stream/imm32
101     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           0xc/disp8       .                 # push *(eax+12)
102     # . . call
103     e8/call  check-ints-equal/disp32
104     # . . discard args
105     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
106     # . end
107     c3/return
108 
109 test-emit-hex-multiple-byte:
110     # setup
111     # . clear-stream(_test-output-stream)
112     # . . push args
113     68/push  _test-output-stream/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     # . clear-stream($_test-output-buffered-file->buffer)
119     # . . push args
120     68/push  $_test-output-buffered-file->buffer/imm32
121     # . . call
122     e8/call  clear-stream/disp32
123     # . . discard args
124     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
125     # emit-hex(_test-output-buffered-file, 0x1234, 2)
126     # . . push args
127     68/push  2/imm32
128     68/push  0x1234/imm32
129     68/push  _test-output-buffered-file/imm32
130     # . . call
131     e8/call  emit-hex/disp32
132     # . . discard args
133     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
134     # flush(_test-output-buffered-file)
135     # . . push args
136     68/push  _test-output-buffered-file/imm32
137     # . . call
138     e8/call  flush/disp32
139     # . . discard args
140     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
141     # check-stream-equal(_test-output-stream, "34 12 ", msg)
142     # . . push args
143     68/push  "F - test-emit-hex-multiple-byte/1"/imm32
144     68/push  "34 12 "/imm32
145     68/push  _test-output-stream/imm32
146     # . . call
147     e8/call  check-stream-equal/disp32
148     # . . discard args
149     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
150     # . end
151     c3/return
152 
153 test-emit-hex-zero-pad:
154     # setup
155     # . clear-stream(_test-output-stream)
156     # . . push args
157     68/push  _test-output-stream/imm32
158     # . . call
159     e8/call  clear-stream/disp32
160     # . . discard args
161     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
162     # . clear-stream($_test-output-buffered-file->buffer)
163     # . . push args
164     68/push  $_test-output-buffered-file->buffer/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     # emit-hex(_test-output-buffered-file, 0xab, 2)
170     # . . push args
171     68/push  2/imm32
172     68/push  0xab/imm32
173     68/push  _test-output-buffered-file/imm32
174     # . . call
175     e8/call  emit-hex/disp32
176     # . . discard args
177     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
178     # flush(_test-output-buffered-file)
179     # . . push args
180     68/push  _test-output-buffered-file/imm32
181     # . . call
182     e8/call  flush/disp32
183     # . . discard args
184     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
185     # check(_test-output-stream->data == 'ab 00 ')
186     # . . push args
187     68/push  "F - test-emit-hex-zero-pad/1"/imm32
188     68/push  "ab 00 "/imm32
189     68/push  _test-output-stream/imm32
190     # . . call
191     e8/call  check-stream-equal/disp32
192     # . . discard args
193     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
194     # . end
195     c3/return
196 
197 test-emit-hex-negative:
198     # setup
199     # . clear-stream(_test-output-stream)
200     # . . push args
201     68/push  _test-output-stream/imm32
202     # . . call
203     e8/call  clear-stream/disp32
204     # . . discard args
205     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
206     # . clear-stream($_test-output-buffered-file->buffer)
207     # . . push args
208     68/push  $_test-output-buffered-file->buffer/imm32
209     # . . call
210     e8/call  clear-stream/disp32
211     # . . discard args
212     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
213     # emit-hex(_test-output-buffered-file, -1, 2)
214     # . . push args
215     68/push  2/imm32
216     68/push  -1/imm32
217     68/push  _test-output-buffered-file/imm32
218     # . . call
219     e8/call  emit-hex/disp32
220     # . . discard args
221     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
222     # flush(_test-output-buffered-file)
223     # . . push args
224     68/push  _test-output-buffered-file/imm32
225     # . . call
226     e8/call  flush/disp32
227     # . . discard args
228     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
229     # check-stream-equal(_test-output-stream == "ff ff ")
230     # . . push args
231     68/push  "F - test-emit-hex-negative/1"/imm32
232     68/push  "ff ff "/imm32
233     68/push  _test-output-stream/imm32
234     # . . call
235     e8/call  check-stream-equal/disp32
236     # . . discard args
237     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
238     # . end
239     c3/return
240 
241 # . . vim:nowrap:textwidth=0