https://github.com/akkartik/mu/blob/master/subx/067print-int.subx
  1 # Print the (hex) textual representation of numbers.
  2 
  3 == code
  4 #   instruction                     effective address                                                   register    displacement    immediate
  5 # . op          subop               mod             rm32          base        index         scale       r32
  6 # . 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
  7 
  8 append-byte-hex:  # f : (address stream), n : int -> <void>
  9     # . prolog
 10     55/push-EBP
 11     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 12     # . save registers
 13     50/push-EAX
 14     # AL = convert upper nibble to hex
 15     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
 16     c1/shift    5/subop/logic-right 3/mod/direct    0/rm32/EAX    .           .             .           .           .               4/imm8            # shift EAX right by 4 bits, while padding zeroes
 17     25/and-EAX  0xf/imm32
 18     # . AL = to-hex-char(AL)
 19     e8/call  to-hex-char/disp32
 20     # append-byte(f, AL)
 21     # . . push args
 22     50/push-EAX
 23     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 24     # . . call
 25     e8/call  append-byte/disp32
 26     # . . discard args
 27     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 28     # AL = convert lower nibble to hex
 29     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
 30     25/and-EAX  0xf/imm32
 31     # . AL = to-hex-char(AL)
 32     e8/call  to-hex-char/disp32
 33     # append-byte(f, AL)
 34     # . . push args
 35     50/push-EAX
 36     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 37     # . . call
 38     e8/call  append-byte/disp32
 39     # . . discard args
 40     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 41 $append-byte-hex:end:
 42     # . restore registers
 43     58/pop-to-EAX
 44     # . epilog
 45     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 46     5d/pop-to-EBP
 47     c3/return
 48 
 49 test-append-byte-hex:
 50     # - check that append-byte-hex adds the hex textual representation
 51     # setup
 52     # . clear-stream(_test-stream)
 53     # . . push args
 54     68/push  _test-stream/imm32
 55     # . . call
 56     e8/call  clear-stream/disp32
 57     # . . discard args
 58     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 59     # append-byte-hex(_test-stream, 0xa)  # exercises digit, non-digit as well as leading zero
 60     # . . push args
 61     68/push  0xa/imm32
 62     68/push  _test-stream/imm32
 63     # . . call
 64     e8/call  append-byte-hex/disp32
 65     # . . discard args
 66     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 67     # check-stream-equal(_test-stream, "0a", msg)
 68     # . . push args
 69     68/push  "F - test-append-byte-hex"/imm32
 70     68/push  "0a"/imm32
 71     68/push  _test-stream/imm32
 72     # . . call
 73     e8/call  check-stream-equal/disp32
 74     # . . discard args
 75     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 76     # . end
 77     c3/return
 78 
 79 # print the hex representation for the lowest byte of a number
 80 print-byte-buffered:  # f : (address buffered-file), n : int -> <void>
 81     # . prolog
 82     55/push-EBP
 83     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 84     # . save registers
 85     50/push-EAX
 86     # AL = convert upper nibble to hex
 87     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
 88     c1/shift    5/subop/logic-right 3/mod/direct    0/rm32/EAX    .           .             .           .           .               4/imm8            # shift EAX right by 4 bits, while padding zeroes
 89     25/and-EAX  0xf/imm32
 90     # . AL = to-hex-char(AL)
 91     e8/call  to-hex-char/disp32
 92     # write-byte-buffered(f, AL)
 93     # . . push args
 94     50/push-EAX
 95     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 96     # . . call
 97     e8/call  write-byte-buffered/disp32
 98     # . . discard args
 99     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
100     # AL = convert lower nibble to hex
101     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
102     25/and-EAX  0xf/imm32
103     # . AL = to-hex-char(AL)
104     e8/call  to-hex-char/disp32
105     # write-byte-buffered(f, AL)
106     # . . push args
107     50/push-EAX
108     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
109     # . . call
110     e8/call  write-byte-buffered/disp32
111     # . . discard args
112     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
113 $print-byte-buffered:end:
114     # . restore registers
115     58/pop-to-EAX
116     # . epilog
117     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
118     5d/pop-to-EBP
119     c3/return
120 
121 test-print-byte-buffered:
122     # - check that print-byte-buffered prints the hex textual representation
123     # setup
124     # . clear-stream(_test-stream)
125     # . . push args
126     68/push  _test-stream/imm32
127     # . . call
128     e8/call  clear-stream/disp32
129     # . . discard args
130     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
131     # . clear-stream(_test-buffered-file+4)
132     # . . push args
133     b8/copy-to-EAX  _test-buffered-file/imm32
134     05/add-to-EAX  4/imm32
135     50/push-EAX
136     # . . call
137     e8/call  clear-stream/disp32
138     # . . discard args
139     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
140     # print-byte-buffered(_test-buffered-file, 0xa)  # exercises digit, non-digit as well as leading zero
141     # . . push args
142     68/push  0xa/imm32
143     68/push  _test-buffered-file/imm32
144     # . . call
145     e8/call  print-byte-buffered/disp32
146     # . . discard args
147     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
148     # flush(_test-buffered-file)
149     # . . push args
150     68/push  _test-buffered-file/imm32
151     # . . call
152     e8/call  flush/disp32
153     # . . discard args
154     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
155     # check-stream-equal(_test-stream, "0a", msg)
156     # . . push args
157     68/push  "F - test-print-byte-buffered"/imm32
158     68/push  "0a"/imm32
159     68/push  _test-stream/imm32
160     # . . call
161     e8/call  check-stream-equal/disp32
162     # . . discard args
163     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
164     # . end
165     c3/return
166 
167 print-int32:  # f : (address stream), n : int -> <void>
168     # pseudocode:
169     #  write(f, "0x")
170     #  ECX = 28
171     #  while true
172     #    if (ECX < 0) break
173     #    EAX = n >> ECX
174     #    EAX = EAX & 0xf
175     #    append-byte(f, AL)
176     #    ECX -= 4
177     #
178     # . prolog
179     55/push-EBP
180     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
181     # . save registers
182     50/push-EAX
183     51/push-ECX
184     # ECX = 28
185     b9/copy-to-ECX  0x1c/imm32
186 $print-int32:print-hex-prefix:
187     # write(f, "0x")
188     # . . push args
189     68/push  "0x"/imm32
190     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
191     # . . call
192     e8/call  write/disp32
193     # . . discard args
194     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
195 $print-int32:loop:
196     # if (ECX < 0) break
197     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0/imm32           # compare ECX
198     7c/jump-if-lesser  $print-int32:end/disp8
199     # EAX = n >> ECX
200     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
201     d3/>>ECX    5/subop/pad-zeroes  3/mod/direct    0/rm32/EAX    .           .             .           .           .               .                 # shift EAX right by ECX bits, padding zeroes
202     # EAX = to-hex-char(AL)
203     25/and-EAX  0xf/imm32
204     e8/call  to-hex-char/disp32
205     # append-byte(f, AL)
206     # . . push args
207     50/push-EAX
208     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
209     # . . call
210     e8/call  append-byte/disp32
211     # . . discard args
212     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
213     # ECX -= 4
214     81          5/subop/subtract    3/mod/direct    1/rm32/ECX    .           .             .           .           .               4/imm32           # subtract from ECX
215     eb/jump  $print-int32:loop/disp8
216 $print-int32:end:
217     # . restore registers
218     59/pop-to-ECX
219     58/pop-to-EAX
220     # . epilog
221     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
222     5d/pop-to-EBP
223     c3/return
224 
225 test-print-int32:
226     # - check that print-int32 prints the hex textual representation
227     # setup
228     # . clear-stream(_test-stream)
229     # . . push args
230     68/push  _test-stream/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     # print-int32(_test-stream, 0x8899aa)
236     # . . push args
237     68/push  0x8899aa/imm32
238     68/push  _test-stream/imm32
239     # . . call
240     e8/call  print-int32/disp32
241     # . . discard args
242     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
243     # check-stream-equal(_test-stream, "0x008899aa", msg)
244     # . . push args
245     68/push  "F - test-print-int32"/imm32
246     68/push  "0x008899aa"/imm32
247     68/push  _test-stream/imm32
248     # . . call
249     e8/call  check-stream-equal/disp32
250     # . . discard args
251     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
252     # . end
253     c3/return
254 
255 # TODO: append to string
256 check-ints-equal2: # (a : int, b : int, msg : (address array byte))
257     # . prolog
258     55/push-EBP
259     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
260     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
261     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
262     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
263     e8/call  check-ints-equal/disp32
264     # . . discard args
265     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32           # add to ESP
266     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
267     # . epilog
268     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
269     5d/pop-to-EBP
270     c3/return
271 
272 print-int32-buffered:  # f : (address buffered-file), n : int -> <void>
273     # pseudocode:
274     #  write-buffered(f, "0x")
275     #  ECX = 28
276     #  while true
277     #    if (ECX < 0) break
278     #    EAX = n >> ECX
279     #    EAX = EAX & 0xf
280     #    write-byte-buffered(f, AL)
281     #    ECX -= 4
282     #
283     # . prolog
284     55/push-EBP
285     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
286     # . save registers
287     50/push-EAX
288     51/push-ECX
289     # ECX = 28
290     b9/copy-to-ECX  0x1c/imm32
291 $print-int32-buffered:print-hex-prefix:
292     # write-buffered(f, "0x")
293     # . . push args
294     68/push  "0x"/imm32
295     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
296     # . . call
297     e8/call  write-buffered/disp32
298     # . . discard args
299     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
300 $print-int32-buffered:loop:
301     # if (ECX < 0) break
302     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0/imm32           # compare ECX
303     7c/jump-if-lesser  $print-int32-buffered:end/disp8
304     # EAX = n >> ECX
305     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
306     d3/>>ECX    5/subop/pad-zeroes  3/mod/direct    0/rm32/EAX    .           .             .           .           .               .                 # shift EAX right by ECX bits, padding zeroes
307     # EAX = to-hex-char(AL)
308     25/and-EAX  0xf/imm32
309     e8/call  to-hex-char/disp32
310     # write-byte-buffered(f, AL)
311     # . . push args
312     50/push-EAX
313     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
314     # . . call
315     e8/call  write-byte-buffered/disp32
316     # . . discard args
317     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
318     # ECX -= 4
319     81          5/subop/subtract    3/mod/direct    1/rm32/ECX    .           .             .           .           .               4/imm32           # subtract from ECX
320     eb/jump  $print-int32-buffered:loop/disp8
321 $print-int32-buffered:end:
322     # . restore registers
323     59/pop-to-ECX
324     58/pop-to-EAX
325     # . epilog
326     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
327     5d/pop-to-EBP
328     c3/return
329 
330 test-print-int32-buffered:
331     # - check that print-int32-buffered prints the hex textual representation
332     # setup
333     # . clear-stream(_test-stream)
334     # . . push args
335     68/push  _test-stream/imm32
336     # . . call
337     e8/call  clear-stream/disp32
338     # . . discard args
339     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
340     # . clear-stream(_test-buffered-file+4)
341     # . . push args
342     b8/copy-to-EAX  _test-buffered-file/imm32
343     05/add-to-EAX  4/imm32
344     50/push-EAX
345     # . . call
346     e8/call  clear-stream/disp32
347     # . . discard args
348     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
349     # print-int32-buffered(_test-buffered-file, 0x8899aa)
350     # . . push args
351     68/push  0x8899aa/imm32
352     68/push  _test-buffered-file/imm32
353     # . . call
354     e8/call  print-int32-buffered/disp32
355     # . . discard args
356     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
357     # flush(_test-buffered-file)
358     # . . push args
359     68/push  _test-buffered-file/imm32
360     # . . call
361     e8/call  flush/disp32
362     # . . discard args
363     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
364 +-- 18 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
382     # check-stream-equal(_test-stream, "0x008899aa", msg)
383     # . . push args
384     68/push  "F - test-print-int32-buffered"/imm32
385     68/push  "0x008899aa"/imm32
386     68/push  _test-stream/imm32
387     # . . call
388     e8/call  check-stream-equal/disp32
389     # . . discard args
390     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
391     # . end
392     c3/return
393 
394 # . . vim:nowrap:textwidth=0