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