https://github.com/akkartik/mu/blob/master/subx/apps/hex.subx
  1 # Read a text file containing whitespace-separated ascii hex bytes from stdin,
  2 # and convert them into a binary file.
  3 #
  4 # To run (from the subx/ directory):
  5 #   $ ./subx translate *.subx apps/hex.subx -o apps/hex
  6 #   $ echo '80 81 82  # comment'  |./subx run apps/hex  |xxd -
  7 # Expected output:
  8 #   09000000: 8081 82
  9 #
 10 # Only hex bytes and comments are permitted. Outside of comments all words
 11 # must be exactly 2 characters long and contain only characters [0-9a-f]. No
 12 # uppercase hex.
 13 
 14 == code
 15 #   instruction                     effective address                                                   register    displacement    immediate
 16 # . op          subop               mod             rm32          base        index         scale       r32
 17 # . 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
 18 
 19     # for debugging: run a single test
 20 #?     e8/call test-skip-until-newline/disp32
 21 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 22 #?     eb/jump  $main:end/disp8
 23 
 24 # main: run tests if necessary, convert stdin if not
 25     # . prolog
 26     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 27     # - if argc > 1 and argv[1] == "test" then return run_tests()
 28     # . argc > 1
 29     81          7/subop/compare     1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0/disp8         1/imm32           # compare *EBP
 30     7e/jump-if-lesser-or-equal  $run-main/disp8
 31     # . argv[1] == "test"
 32     # . . push args
 33     68/push  "test"/imm32
 34     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       .                 # push *(EBP+8)
 35     # . . call
 36     e8/call  kernel-string-equal/disp32
 37     # . . discard args
 38     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 39     # . check result
 40     3d/compare-EAX  1/imm32
 41     75/jump-if-not-equal  $run-main/disp8
 42     # . run-tests()
 43 #?     e8/call test-hex-below-0/disp32
 44 #?     e8/call test-scan-next-byte/disp32
 45 #?     e8/call test-scan-next-byte-skips-comment/disp32
 46     e8/call  run-tests/disp32
 47     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 48     eb/jump  $main:end/disp8
 49 $run-main:
 50     # - otherwise convert stdin
 51     # var ed/EAX : exit-descriptor
 52     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
 53     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           0/r32/EAX   .               .                 # copy ESP to EAX
 54     # configure ed to really exit()
 55     # . ed->target = 0
 56     c7/copy                         0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
 57     # return convert(Stdin, 1/stdout, 2/stderr, ed)
 58     # . . push args
 59     50/push-EAX/ed
 60     68/push  2/imm32/stderr
 61     68/push  1/imm32/stdout
 62     68/push  Stdin/imm32
 63     # . . call
 64     e8/call  convert/disp32
 65     # . . discard args
 66     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
 67     # . syscall(exit, 0)
 68     bb/copy-to-EBX  0/imm32
 69 $main:end:
 70     b8/copy-to-EAX  1/imm32/exit
 71     cd/syscall  0x80/imm8
 72 
 73 # the main entry point
 74 convert:  # in : (address buffered-file), out : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> <void>
 75     # pseudocode:
 76     #   repeatedly
 77     #     EAX = convert-next-hex-byte(in, err, ed)
 78     #     if EAX == 0xffffffff break  # eof
 79     #     write-byte(out, EAX)
 80     #   flush(out)
 81     #
 82     # . prolog
 83     55/push-EBP
 84     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 85     # . save registers
 86     # var buf/ECX : (address stream) on the stack
 87     # It occupies 1KB.
 88     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x400/imm32       # subtract from ESP
 89     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           1/r32/ECX   .               .                 # copy ESP to ECX
 90     # initialize the stream
 91     # . length = 1KB - 12 bytes for 'read', 'write' and 'length' fields.
 92     c7/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           .           8/disp8         0x3f4/imm32       # copy to *(ECX+8)
 93     # . clear-stream(buf)
 94     # . . push args
 95     51/push-ECX
 96     # . . call
 97     e8/call  clear-stream/disp32
 98     # . . discard args
 99     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
100 $convert:loop:
101     # . restore registers
102     # . epilog
103     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
104     5d/pop-to-EBP
105     c3/return
106 
107 # read bytes from 'in' until a sequence of two lowercase hex characters (0-9, a-f)
108 # skip spaces and newlines
109 # on '#' skip bytes until newline
110 # raise an error and abort on all other unexpected bytes
111 # return the binary value of the two hex characters in EAX
112 # return 0xffffffff on end of file
113 convert-next-hex-byte:  # in : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> byte-or-eof/EAX
114     # pseudocode:
115     #   EAX = scan-next-byte(in, err, ed)
116     #   if (EAX == 0xffffffff) return
117     #   ECX = EAX
118     #   EAX = scan-next-byte(in, err, ed)
119     #   if (EAX == 0xffffffff) error("partial byte found")
120     #   ECX = (ECX << 8) | EAX
121     #   return
122     #
123     # . prolog
124     55/push-EBP
125     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
126     # . save registers
127     # var buf/ECX : (address stream) on the stack
128     # It occupies 1KB.
129     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x400/imm32       # subtract from ESP
130     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           1/r32/ECX   .               .                 # copy ESP to ECX
131     # initialize the stream
132     # . length = 1KB - 12 bytes for 'read', 'write' and 'length' fields.
133     c7/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           .           8/disp8         0x3f4/imm32       # copy to *(ECX+8)
134     # . clear-stream(buf)
135     # . . push args
136     51/push-ECX
137     # . . call
138     e8/call  clear-stream/disp32
139     # . . discard args
140     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
141 $convert:loop:
142     # . restore registers
143     # . epilog
144     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
145     5d/pop-to-EBP
146     c3/return
147 
148 # read whitespace until a hex byte, and return it
149 # return 0xffffffff if file ends without finding a hex byte
150 # on '#' skip all bytes until newline
151 # abort on any other byte
152 scan-next-byte:  # in : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> byte-or-eof/EAX
153     # pseudocode:
154     #   repeatedly
155     #     EAX = read-byte(in)
156     #     if is-hex-lowercase-byte?(EAX) return EAX
157     #     if EAX == 0x20 continue
158     #     if EAX == '#' skip-until-newline(in)
159     #     else error-byte(ed, err, "unexpected byte: " EAX)
160     #   return 0xffffffff
161     #
162     # . prolog
163     55/push-EBP
164     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
165     # . save registers
166 $scan-next-byte:loop:
167     # EAX = read-byte(in)
168     # . . push args
169     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       .                 # push *(EBP+8)
170     # . . call
171     e8/call  read-byte/disp32
172     # . . discard args
173     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
174     # if is-hex-lowercase-byte?(EAX) return EAX
175     # . save EAX for now
176     50/push-EAX
177     # . is-hex-lowercase-byte?(EAX)
178     # . . push args
179     50/push-EAX
180     # . . call
181     e8/call  is-hex-lowercase-byte?/disp32
182     # . . discard args
183     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
184     # . compare with 'false'
185     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
186     # . restore EAX (does not affect flags)
187     58/pop-to-EAX
188     # . check whether to return
189     75/jump-if-not-equal  $scan-next-byte:end/disp8
190 $scan-next-byte:check1:
191     # if EAX == ' ' continue
192     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0x20/imm32        # compare EAX
193     74/jump-if-equal  $scan-next-byte:loop/disp8
194     # if EAX == '\t' continue
195     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0x9/imm32         # compare EAX
196     74/jump-if-equal  $scan-next-byte:loop/disp8
197     # if EAX == '\n' continue
198     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xa/imm32         # compare EAX
199     74/jump-if-equal  $scan-next-byte:loop/disp8
200 $scan-next-byte:check2:
201     # if EAX == '#' skip-until-newline(in)
202     3d/compare-with-EAX  0x23/imm32
203     75/jump-if-not-equal  $scan-next-byte:check3/disp8
204     # . skip-until-newline(in)
205     # . . push args
206     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       .                 # push *(EBP+8)
207     # . . call
208     e8/call  skip-until-newline/disp32
209     # . . discard args
210     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
211     eb/jump  $scan-next-byte:loop/disp8
212 $scan-next-byte:check3:
213 # TODO: error-byte takes a buffered-file, not a (fd or (address stream))
214     # otherwise error-byte(ed, err, msg, EAX)
215     # . . push args
216     50/push-EAX
217     68/push  "scan-next-byte: invalid byte"/imm32
218     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
219     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x10/disp8      .                 # push *(EBP+16)
220     # . . call
221     e8/call  error-byte/disp32  # never returns
222 $scan-next-byte:end:
223     # . restore registers
224     # . epilog
225     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
226     5d/pop-to-EBP
227     c3/return
228 
229 test-scan-next-byte:
230     # - check that the first two bytes of the input are assembled into the resulting number
231     # setup
232     # . clear-stream(_test-stream)
233     # . . push args
234     68/push  _test-stream/imm32
235     # . . call
236     e8/call  clear-stream/disp32
237     # . . discard args
238     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
239     # . clear-stream(_test-buffered-file+4)
240     # . . push args
241     b8/copy-to-EAX  _test-buffered-file/imm32
242     05/add-to-EAX  4/imm32
243     50/push-EAX
244     # . . call
245     e8/call  clear-stream/disp32
246     # . . discard args
247     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
248     # initialize '_test-stream' to "abc"
249     # . write(_test-stream, "abc")
250     # . . push args
251     68/push  "abc"/imm32
252     68/push  _test-stream/imm32
253     # . . call
254     e8/call  write/disp32
255     # . . discard args
256     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
257     # scan-next-byte(_test-buffered-file)
258     # . . push args
259     68/push  _test-buffered-file/imm32
260     # . . call
261     e8/call  scan-next-byte/disp32
262     # . . discard args
263     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
264     # check-ints-equal(EAX, 0x61/a, msg)
265     # . . push args
266     68/push  "F - test-scan-next-byte"/imm32
267     68/push  0x61/imm32/a
268     50/push-EAX
269     # . . call
270     e8/call  check-ints-equal/disp32
271     # . . discard args
272     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
273     # . end
274     c3/return
275 
276 test-scan-next-byte-skips-whitespace:
277     # - check that the first two bytes of the input are assembled into the resulting number
278     # setup
279     # . clear-stream(_test-stream)
280     # . . push args
281     68/push  _test-stream/imm32
282     # . . call
283     e8/call  clear-stream/disp32
284     # . . discard args
285     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
286     # . clear-stream(_test-buffered-file+4)
287     # . . push args
288     b8/copy-to-EAX  _test-buffered-file/imm32
289     05/add-to-EAX  4/imm32
290     50/push-EAX
291     # . . call
292     e8/call  clear-stream/disp32
293     # . . discard args
294     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
295     # initialize '_test-stream' to input with leading whitespace
296     # . write(_test-stream, text)
297     # . . push args
298     68/push  "  abc"/imm32
299     68/push  _test-stream/imm32
300     # . . call
301     e8/call  write/disp32
302     # . . discard args
303     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
304     # scan-next-byte(_test-buffered-file)
305     # . . push args
306     68/push  _test-buffered-file/imm32
307     # . . call
308     e8/call  scan-next-byte/disp32
309     # . . discard args
310     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
311     # check-ints-equal(EAX, 0x61/a, msg)
312     # . . push args
313     68/push  "F - test-scan-next-byte-skips-whitespace"/imm32
314     68/push  0x61/imm32/a
315     50/push-EAX
316     # . . call
317     e8/call  check-ints-equal/disp32
318     # . . discard args
319     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
320     # . end
321     c3/return
322 
323 test-scan-next-byte-skips-comment:
324     # - check that the first two bytes of the input are assembled into the resulting number
325     # setup
326     # . clear-stream(_test-stream)
327     # . . push args
328     68/push  _test-stream/imm32
329     # . . call
330     e8/call  clear-stream/disp32
331     # . . discard args
332     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
333     # . clear-stream(_test-buffered-file+4)
334     # . . push args
335     b8/copy-to-EAX  _test-buffered-file/imm32
336     05/add-to-EAX  4/imm32
337     50/push-EAX
338     # . . call
339     e8/call  clear-stream/disp32
340     # . . discard args
341     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
342     # initialize '_test-stream' to input with leading comment
343     # . write(_test-stream, comment)
344     # . . push args
345     68/push  "#x"/imm32
346     68/push  _test-stream/imm32
347     # . . call
348     e8/call  write/disp32
349     # . . discard args
350     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
351     # . write(_test-stream, Newline)
352     # . . push args
353     68/push  Newline/imm32
354     68/push  _test-stream/imm32
355     # . . call
356     e8/call  write/disp32
357     # . . discard args
358     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
359     # . write(_test-stream, real text)
360     # . . push args
361     68/push  "ab"/imm32
362     68/push  _test-stream/imm32
363     # . . call
364     e8/call  write/disp32
365     # . . discard args
366     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
367     # scan-next-byte(_test-buffered-file)
368     # . . push args
369     68/push  _test-buffered-file/imm32
370     # . . call
371     e8/call  scan-next-byte/disp32
372     # . . discard args
373     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
374     # check-ints-equal(EAX, 0x61/a, msg)
375     # . . push args
376     68/push  "F - test-scan-next-byte-skips-comment"/imm32
377     68/push  0x61/imm32/a
378     50/push-EAX
379     # . . call
380     e8/call  check-ints-equal/disp32
381     # . . discard args
382     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
383     # . end
384     c3/return
385 
386 test-scan-next-byte-skips-comment-and-whitespace:
387     # - check that the first two bytes of the input are assembled into the resulting number
388     # setup
389     # . clear-stream(_test-stream)
390     # . . push args
391     68/push  _test-stream/imm32
392     # . . call
393     e8/call  clear-stream/disp32
394     # . . discard args
395     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
396     # . clear-stream(_test-buffered-file+4)
397     # . . push args
398     b8/copy-to-EAX  _test-buffered-file/imm32
399     05/add-to-EAX  4/imm32
400     50/push-EAX
401     # . . call
402     e8/call  clear-stream/disp32
403     # . . discard args
404     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
405     # initialize '_test-stream' to input with leading comment and more whitespace after newline
406     # . write(_test-stream, comment)
407     # . . push args
408     68/push  "#x"/imm32
409     68/push  _test-stream/imm32
410     # . . call
411     e8/call  write/disp32
412     # . . discard args
413     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
414     # . write(_test-stream, Newline)
415     # . . push args
416     68/push  Newline/imm32
417     68/push  _test-stream/imm32
418     # . . call
419     e8/call  write/disp32
420     # . . discard args
421     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
422     # . write(_test-stream, real text)
423     # . . push args
424     68/push  " ab"/imm32
425     68/push  _test-stream/imm32
426     # . . call
427     e8/call  write/disp32
428     # . . discard args
429     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
430     # scan-next-byte(_test-buffered-file)
431     # . . push args
432     68/push  _test-buffered-file/imm32
433     # . . call
434     e8/call  scan-next-byte/disp32
435     # . . discard args
436     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
437     # check-ints-equal(EAX, 0x61/a, msg)
438     # . . push args
439     68/push  "F - test-scan-next-byte-skips-comment"/imm32
440     68/push  0x61/imm32/a
441     50/push-EAX
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-scan-next-byte-skips-whitespace-and-comment:
450     # - check that the first two bytes of the input are assembled into the resulting number
451     # setup
452     # . clear-stream(_test-stream)
453     # . . push args
454     68/push  _test-stream/imm32
455     # . . call
456     e8/call  clear-stream/disp32
457     # . . discard args
458     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
459     # . clear-stream(_test-buffered-file+4)
460     # . . push args
461     b8/copy-to-EAX  _test-buffered-file/imm32
462     05/add-to-EAX  4/imm32
463     50/push-EAX
464     # . . call
465     e8/call  clear-stream/disp32
466     # . . discard args
467     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
468     # initialize '_test-stream' to input with leading whitespace and comment
469     # . write(_test-stream, comment)
470     # . . push args
471     68/push  " #x"/imm32
472     68/push  _test-stream/imm32
473     # . . call
474     e8/call  write/disp32
475     # . . discard args
476     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
477     # . write(_test-stream, Newline)
478     # . . push args
479     68/push  Newline/imm32
480     68/push  _test-stream/imm32
481     # . . call
482     e8/call  write/disp32
483     # . . discard args
484     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
485     # . write(_test-stream, real text)
486     # . . push args
487     68/push  "ab"/imm32
488     68/push  _test-stream/imm32
489     # . . call
490     e8/call  write/disp32
491     # . . discard args
492     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
493     # scan-next-byte(_test-buffered-file)
494     # . . push args
495     68/push  _test-buffered-file/imm32
496     # . . call
497     e8/call  scan-next-byte/disp32
498     # . . discard args
499     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
500     # check-ints-equal(EAX, 0x61/a, msg)
501     # . . push args
502     68/push  "F - test-scan-next-byte-skips-comment"/imm32
503     68/push  0x61/imm32/a
504     50/push-EAX
505     # . . call
506     e8/call  check-ints-equal/disp32
507     # . . discard args
508     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
509     # . end
510     c3/return
511 
512 test-scan-next-byte-reads-final-byte:
513     # - check that the first two bytes of the input are assembled into the resulting number
514     # setup
515     # . clear-stream(_test-stream)
516     # . . push args
517     68/push  _test-stream/imm32
518     # . . call
519     e8/call  clear-stream/disp32
520     # . . discard args
521     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
522     # . clear-stream(_test-buffered-file+4)
523     # . . push args
524     b8/copy-to-EAX  _test-buffered-file/imm32
525     05/add-to-EAX  4/imm32
526     50/push-EAX
527     # . . call
528     e8/call  clear-stream/disp32
529     # . . discard args
530     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
531     # initialize '_test-stream' to input with single character
532     # . write(_test-stream, character)
533     # . . push args
534     68/push  "a"/imm32
535     68/push  _test-stream/imm32
536     # . . call
537     e8/call  write/disp32
538     # . . discard args
539     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
540     # scan-next-byte(_test-buffered-file)
541     # . . push args
542     68/push  _test-buffered-file/imm32
543     # . . call
544     e8/call  scan-next-byte/disp32
545     # . . discard args
546     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
547     # check-ints-equal(EAX, 0x61/a, msg)
548     # . . push args
549     68/push  "F - test-scan-next-byte-skips-comment"/imm32
550     68/push  0x61/imm32/a
551     50/push-EAX
552     # . . call
553     e8/call  check-ints-equal/disp32
554     # . . discard args
555     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
556     # . end
557     c3/return
558 
559 is-hex-lowercase-byte?:  # c : byte -> bool/EAX
560     # . prolog
561     55/push-EBP
562     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
563     # . save registers
564     51/push-ECX
565     # ECX = c
566     8b/copy                         1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           1/r32/ECX   0x8/disp8       .                 # copy *(EBP+8) to ECX
567     # return false if c < '0'
568     b8/copy-to-EAX  0/imm32/false
569     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x30/imm32        # compare ECX
570     7c/jump-if-lesser  $is-hex-lowercase-byte?:end/disp8
571     # return false if c > 'f'
572     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x66/imm32        # compare ECX
573     7f/jump-if-greater  $is-hex-lowercase-byte?:end/disp8
574     # return true if c <= '9'
575     b8/copy-to-EAX  1/imm32/true
576     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x39/imm32        # compare ECX
577     7e/jump-if-lesser-or-equal  $is-hex-lowercase-byte?:end/disp8
578     # return true if c >= 'a'
579     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x61/imm32        # compare ECX
580     7d/jump-if-greater-or-equal  $is-hex-lowercase-byte?:end/disp8
581     # otherwise return false
582     b8/copy-to-EAX  0/imm32/false
583 $is-hex-lowercase-byte?:end:
584     # . restore registers
585     59/pop-to-ECX
586     # . epilog
587     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
588     5d/pop-to-EBP
589     c3/return
590 
591 test-hex-below-0:
592     # is-hex-lowercase-byte?(0x2f)
593     # . . push args
594     68/push  0x2f/imm32
595     # . . call
596     e8/call  is-hex-lowercase-byte?/disp32
597     # . . discard args
598     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
599     # check-ints-equal(EAX, 0, msg)
600     # . . push args
601     68/push  "F - test-hex-below-0"/imm32
602     68/push  0/imm32/false
603     50/push-EAX
604     # . . call
605     e8/call  check-ints-equal/disp32
606     # . . discard args
607     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
608     c3/return
609 
610 test-hex-0-to-9:
611     # is-hex-lowercase-byte?(0x30)
612     # . . push args
613     68/push  0x30/imm32
614     # . . call
615     e8/call  is-hex-lowercase-byte?/disp32
616     # . . discard args
617     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
618     # check-ints-equal(EAX, 1, msg)
619     # . . push args
620     68/push  "F - test-hex-at-0"/imm32
621     68/push  1/imm32/true
622     50/push-EAX
623     # . . call
624     e8/call  check-ints-equal/disp32
625     # . . discard args
626     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
627     # is-hex-lowercase-byte?(0x39)
628     # . . push args
629     68/push  0x39/imm32
630     # . . call
631     e8/call  is-hex-lowercase-byte?/disp32
632     # . . discard args
633     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
634     # check-ints-equal(EAX, 1, msg)
635     # . . push args
636     68/push  "F - test-hex-at-9"/imm32
637     68/push  1/imm32/true
638     50/push-EAX
639     # . . call
640     e8/call  check-ints-equal/disp32
641     # . . discard args
642     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
643     c3/return
644 
645 test-hex-above-9-to-a:
646     # is-hex-lowercase-byte?(0x3a)
647     # . . push args
648     68/push  0x3a/imm32
649     # . . call
650     e8/call  is-hex-lowercase-byte?/disp32
651     # . . discard args
652     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
653     # check-ints-equal(EAX, 0, msg)
654     # . . push args
655     68/push  "F - test-hex-above-9-to-a"/imm32
656     68/push  0/imm32/false
657     50/push-EAX
658     # . . call
659     e8/call  check-ints-equal/disp32
660     # . . discard args
661     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
662     c3/return
663 
664 test-hex-a-to-f:
665     # is-hex-lowercase-byte?(0x61)
666     # . . push args
667     68/push  0x61/imm32
668     # . . call
669     e8/call  is-hex-lowercase-byte?/disp32
670     # . . discard args
671     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
672     # check-ints-equal(EAX, 1, msg)
673     # . . push args
674     68/push  "F - test-hex-at-a"/imm32
675     68/push  1/imm32/true
676     50/push-EAX
677     # . . call
678     e8/call  check-ints-equal/disp32
679     # . . discard args
680     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
681     # is-hex-lowercase-byte?(0x66)
682     # . . push args
683     68/push  0x66/imm32
684     # . . call
685     e8/call  is-hex-lowercase-byte?/disp32
686     # . . discard args
687     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
688     # check-ints-equal(EAX, 1, msg)
689     # . . push args
690     68/push  "F - test-hex-at-f"/imm32
691     68/push  1/imm32/true
692     50/push-EAX
693     # . . call
694     e8/call  check-ints-equal/disp32
695     # . . discard args
696     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
697     c3/return
698 
699 test-hex-above-f:
700     # is-hex-lowercase-byte?(0x67)
701     # . . push args
702     68/push  0x67/imm32
703     # . . call
704     e8/call  is-hex-lowercase-byte?/disp32
705     # . . discard args
706     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
707     # check-ints-equal(EAX, 0, msg)
708     # . . push args
709     68/push  "F - test-hex-above-f"/imm32
710     68/push  0/imm32/false
711     50/push-EAX
712     # . . call
713     e8/call  check-ints-equal/disp32
714     # . . discard args
715     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
716     c3/return
717 
718 skip-until-newline:  # in : (address buffered-file) -> <void>
719     # pseudocode:
720     #   push EAX
721     #   repeatedly:
722     #     EAX = read-byte(in)
723     #     if EAX == 0xffffffff break
724     #     if EAX == 0x0a break
725     #   pop EAX
726     # . prolog
727     55/push-EBP
728     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
729     # . save registers
730     50/push-EAX
731 $skip-until-newline:loop:
732     # . EAX = read-byte(in)
733     # . . push args
734     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       .                 # push *(EBP+8)
735     # . . call
736     e8/call  read-byte/disp32
737     # . . discard args
738     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
739     # . if EAX == 0xffffffff break
740     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xffffffff/imm32  # compare EAX
741     74/jump-if-equal  $skip-until-newline:end/disp8
742 $aa:
743     # . if EAX != 0xa/newline loop
744     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xa/imm32         # compare EAX
745     75/jump-if-not-equal  $skip-until-newline:loop/disp8
746 $skip-until-newline:end:
747     # . restore registers
748     58/pop-to-EAX
749     # . epilog
750     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
751     5d/pop-to-EBP
752     c3/return
753 
754 test-skip-until-newline:
755     # - check that the read pointer points after the newline
756     # setup
757     # . clear-stream(_test-stream)
758     # . . push args
759     68/push  _test-stream/imm32
760     # . . call
761     e8/call  clear-stream/disp32
762     # . . discard args
763     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
764     # . clear-stream(_test-buffered-file+4)
765     # . . push args
766     b8/copy-to-EAX  _test-buffered-file/imm32
767     05/add-to-EAX  4/imm32
768     50/push-EAX
769     # . . call
770     e8/call  clear-stream/disp32
771     # . . discard args
772     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
773     # initialize '_test-stream' to "abc\nde"
774     # . write(_test-stream, "abc")
775     # . . push args
776     68/push  "abc"/imm32
777     68/push  _test-stream/imm32
778     # . . call
779     e8/call  write/disp32
780     # . . discard args
781     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
782     # . write(_test-stream, Newline)
783     # . . push args
784     68/push  Newline/imm32
785     68/push  _test-stream/imm32
786     # . . call
787     e8/call  write/disp32
788     # . . discard args
789     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
790     # . write(_test-stream, "de")
791     # . . push args
792     68/push  "de"/imm32
793     68/push  _test-stream/imm32
794     # . . call
795     e8/call  write/disp32
796     # . . discard args
797     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
798     # skip-until-newline(_test-buffered-file)
799     # . . push args
800     68/push  _test-buffered-file/imm32
801     # . . call
802     e8/call  skip-until-newline/disp32
803     # . . discard args
804     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
805     # check-ints-equal(_test-buffered-file->read, 4, msg)
806     # . . push args
807     68/push  "F - test-skip-until-newline"/imm32
808     68/push  4/imm32
809     b8/copy-to-EAX  _test-buffered-file/imm32
810     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
811     # . . call
812     e8/call  check-ints-equal/disp32
813     # . . discard args
814     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
815     # . end
816     c3/return
817 
818 == data
819 
820 # . . vim:nowrap:textwidth=0