https://github.com/akkartik/mu/blob/master/subx/058stream-equal.subx
  1 # some primitives for checking stream contents
  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 # main:
  9 #?     e8/call test-next-stream-line-equal-stops-at-newline/disp32
 10     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
 11     # syscall(exit, Num-test-failures)
 12     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 13     b8/copy-to-EAX  1/imm32/exit
 14     cd/syscall  0x80/imm8
 15 
 16 # compare all the data in a stream (ignoring the read pointer)
 17 stream-data-equal?:  # f : (address stream), s : (address string) -> EAX : boolean
 18     # . prolog
 19     55/push-EBP
 20     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 21     # . save registers
 22     51/push-ECX
 23     52/push-EDX
 24     56/push-ESI
 25     57/push-EDI
 26     # ESI = f
 27     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
 28     # max/EDX = f->data + f->write
 29     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
 30     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  0/index/EAX   .           2/r32/EDX   0xc/disp8       .                 # copy ESI+EAX+12 to EDX
 31     # currf/ESI = f->data
 32     81          0/subop/add         3/mod/direct    6/rm32/ESI    .           .             .           .           .               0xc/imm32         # add to ESI
 33     # EDI = s
 34     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
 35     # if (f->write != s->length) return false
 36     39/compare                      0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # compare *EDI and EAX
 37     75/jump-if-not-equal  $stream-data-equal?:false/disp8
 38     # currs/EDI = s->data
 39     81          0/subop/add         3/mod/direct    7/rm32/EDI    .           .             .           .           .               4/imm32           # add to EDI
 40     # EAX = ECX = 0
 41     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 42     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
 43 $stream-data-equal?:loop:
 44     # if (curr >= max) return true
 45     39/compare                      3/mod/direct    6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # compare ESI with EDX
 46     7d/jump-if-greater-or-equal  $stream-data-equal?:true/disp8
 47     # AL = *currs
 48     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
 49     # CL = *curr
 50     8a/copy-byte                    0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/CL    .               .                 # copy byte at *EDI to CL
 51     # if (EAX != ECX) return false
 52     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX and ECX
 53     75/jump-if-not-equal  $stream-data-equal?:false/disp8
 54     # ++f
 55     46/increment-ESI
 56     # ++curr
 57     47/increment-EDI
 58     eb/jump $stream-data-equal?:loop/disp8
 59 $stream-data-equal?:false:
 60     b8/copy-to-EAX  0/imm32
 61     eb/jump  $stream-data-equal?:end/disp8
 62 $stream-data-equal?:true:
 63     b8/copy-to-EAX  1/imm32
 64 $stream-data-equal?:end:
 65     # . restore registers
 66     5f/pop-to-EDI
 67     5e/pop-to-ESI
 68     5a/pop-to-EDX
 69     59/pop-to-ECX
 70     # . epilog
 71     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 72     5d/pop-to-EBP
 73     c3/return
 74 
 75 test-stream-data-equal:
 76     # . prolog
 77     55/push-EBP
 78     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 79     # clear-stream(_test-stream)
 80     # . . push args
 81     68/push  _test-stream/imm32
 82     # . . call
 83     e8/call  clear-stream/disp32
 84     # . . discard args
 85     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 86     # write(_test-stream, "Abc")
 87     # . . push args
 88     68/push  "Abc"/imm32
 89     68/push  _test-stream/imm32
 90     # . . call
 91     e8/call  write/disp32
 92     # . . discard args
 93     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 94     # EAX = stream-data-equal?(_test-stream, "Abc")
 95     # . . push args
 96     68/push  "Abc"/imm32
 97     68/push  _test-stream/imm32
 98     # . . call
 99     e8/call  stream-data-equal?/disp32
100     # . . discard args
101     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
102     # check-ints-equal(EAX, 1, msg)
103     # . . push args
104     68/push  "F - test-stream-data-equal"/imm32
105     68/push  1/imm32
106     50/push-EAX
107     # . . call
108     e8/call  check-ints-equal/disp32
109     # . . discard args
110     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
111     # . epilog
112     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
113     5d/pop-to-EBP
114     c3/return
115 
116 test-stream-data-equal-2:
117     # . prolog
118     55/push-EBP
119     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
120     # clear-stream(_test-stream)
121     # . . push args
122     68/push  _test-stream/imm32
123     # . . call
124     e8/call  clear-stream/disp32
125     # . . discard args
126     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
127     # write(_test-stream, "Abc")
128     # . . push args
129     68/push  "Abc"/imm32
130     68/push  _test-stream/imm32
131     # . . call
132     e8/call  write/disp32
133     # . . discard args
134     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
135     # EAX = stream-data-equal?(_test-stream, "Abd")
136     # . . push args
137     68/push  "Abd"/imm32
138     68/push  _test-stream/imm32
139     # . . call
140     e8/call  stream-data-equal?/disp32
141     # . . discard args
142     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
143     # check-ints-equal(EAX, 0, msg)
144     # . . push args
145     68/push  "F - test-stream-data-equal-2"/imm32
146     68/push  0/imm32
147     50/push-EAX
148     # . . call
149     e8/call  check-ints-equal/disp32
150     # . . discard args
151     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
152     # . epilog
153     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
154     5d/pop-to-EBP
155     c3/return
156 
157 test-stream-data-equal-length-check:
158     # . prolog
159     55/push-EBP
160     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
161     # clear-stream(_test-stream)
162     # . . push args
163     68/push  _test-stream/imm32
164     # . . call
165     e8/call  clear-stream/disp32
166     # . . discard args
167     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
168     # write(_test-stream, "Abc")
169     # . . push args
170     68/push  "Abc"/imm32
171     68/push  _test-stream/imm32
172     # . . call
173     e8/call  write/disp32
174     # . . discard args
175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
176     # EAX = stream-data-equal?(_test-stream, "Abcd")
177     # . . push args
178     68/push  "Abcd"/imm32
179     68/push  _test-stream/imm32
180     # . . call
181     e8/call  stream-data-equal?/disp32
182     # . . discard args
183     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
184     # check-ints-equal(EAX, 0, msg)
185     # . . push args
186     68/push  "F - test-stream-data-equal-length-check"/imm32
187     68/push  0/imm32
188     50/push-EAX
189     # . . call
190     e8/call  check-ints-equal/disp32
191     # . . discard args
192     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
193     # . epilog
194     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
195     5d/pop-to-EBP
196     c3/return
197 
198 # helper for later tests
199 check-stream-equal:  # f : (address stream), s : (address string), msg : (address string)
200     # . prolog
201     55/push-EBP
202     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
203     # . save registers
204     50/push-EAX
205     # EAX = stream-data-equal?(f, s)
206     # . . push args
207     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
208     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
209     # . . call
210     e8/call  stream-data-equal?/disp32
211     # . . discard args
212     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
213     # check-ints-equal(EAX, 1, msg)
214     # . . push args
215     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
216     68/push  1/imm32
217     50/push-EAX
218     # . . call
219     e8/call  check-ints-equal/disp32
220     # . . discard args
221     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
222     # . restore registers
223     58/pop-to-EAX
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 # scan the next line until newline starting from f->read and compare it with
230 # 's' (ignoring the trailing newline)
231 # on success, set f->read to after the next newline
232 # on failure, leave f->read unmodified
233 # this function is usually used only in tests, so we repeatedly write f->read
234 next-stream-line-equal?:  # f : (address stream), s : (address string) -> EAX : boolean
235     # pseudocode:
236     #   currf = f->read  # bound: f->write
237     #   currs = 0  # bound : s->length
238     #   while true:
239     #     if (currf >= f->write)
240     #       return currs >= s->length
241     #     if (f[currf] == '\n')
242     #       ++currf
243     #       return currs >= s->length
244     #     if (currs >= s->length) return false  # the current line of f still has data to match
245     #     if (f[currf] != s[currs]) return false
246     #     ++currf
247     #     ++currs
248     #
249     # collapsing the two branches that can return true:
250     #   currf = f->read  # bound: f->write
251     #   currs = 0  # bound : s->length
252     #   while true:
253     #     if (currf >= f->write) break
254     #     if (f[currf] == '\n') break
255     #     if (currs >= s->length) return false  # the current line of f still has data to match
256     #     if (f[currf] != s[currs]) return false
257     #     ++currf
258     #     ++currs
259     #   ++currf  # skip '\n'
260     #   return currs >= s->length
261     # Here the final `++currf` is sometimes unnecessary (if we're already at the end of the stream)
262     #
263     # registers:
264     #   f: ESI
265     #   s: EDI
266     #   currf: ECX
267     #   currs: EDX
268     #   f[currf]: EAX
269     #   s[currs]: EBX
270     #
271     # . prolog
272     55/push-EBP
273     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
274     # . save registers
275     51/push-ECX
276     52/push-EDX
277     56/push-ESI
278     57/push-EDI
279     # ESI = f
280     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
281     # currf/ECX = f->read
282     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
283     # EDI = s
284     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
285     # currs/EDX = 0
286     31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
287     # EAX = EBX = 0
288     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
289     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
290 $next-stream-line-equal?:loop:
291     # if (currf >= f->write) break
292     3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # compare ECX with *ESI
293     7d/jump-if-greater-or-equal  $next-stream-line-equal?:break/disp8
294     # AL = *(f->data + f->read)
295     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
296     # if (EAX == '\n') break
297     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xa/imm32/newline # compare EAX
298     74/jump-if-equal  $next-stream-line-equal?:break/disp8
299     # if (currs >= s->length) return false
300     3b/compare                      0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # compare EDX with *EDI
301     7d/jump-if-greater-or-equal  $next-stream-line-equal?:false/disp8
302     # BL = *(s->data + currs)
303     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    7/base/EDI  2/index/EDX   .           3/r32/BL    4/disp8         .                 # copy byte at *(EDI+EDX+4) to BL
304     # if (EAX != EBX) return false
305     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # compare EAX and EBX
306     75/jump-if-not-equal  $next-stream-line-equal?:false/disp8
307     # ++currf
308     41/increment-ECX
309     # ++currs
310     42/increment-EDX
311     eb/jump $next-stream-line-equal?:loop/disp8
312 $next-stream-line-equal?:break:
313     # ++currf
314     41/increment-ECX
315     # if (currs >= s->length) return true
316     3b/compare                      0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # compare EDX with *EDI
317     7c/jump-if-lesser  $next-stream-line-equal?:false/disp8
318 $next-stream-line-equal?:true:
319     b8/copy-to-EAX  1/imm32
320     # persist f->read on success
321     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
322     eb/jump  $next-stream-line-equal?:end/disp8
323 $next-stream-line-equal?:false:
324     b8/copy-to-EAX  0/imm32
325 $next-stream-line-equal?:end:
326     # . restore registers
327     5f/pop-to-EDI
328     5e/pop-to-ESI
329     5a/pop-to-EDX
330     59/pop-to-ECX
331     # . epilog
332     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
333     5d/pop-to-EBP
334     c3/return
335 
336 test-next-stream-line-equal-stops-at-newline:
337     # . prolog
338     55/push-EBP
339     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
340     # clear-stream(_test-stream)
341     # . . push args
342     68/push  _test-stream/imm32
343     # . . call
344     e8/call  clear-stream/disp32
345     # . . discard args
346     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
347     # write(_test-stream, "Abc\ndef")
348     # . write(_test-stream, "Abc")
349     # . . push args
350     68/push  "Abc"/imm32
351     68/push  _test-stream/imm32
352     # . . call
353     e8/call  write/disp32
354     # . . discard args
355     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
356     # . write(_test-stream, Newline)
357     # . . push args
358     68/push  Newline/imm32
359     68/push  _test-stream/imm32
360     # . . call
361     e8/call  write/disp32
362     # . . discard args
363     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
364     # . write(_test-stream, "def")
365     # . . push args
366     68/push  "def"/imm32
367     68/push  _test-stream/imm32
368     # . . call
369     e8/call  write/disp32
370     # . . discard args
371     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
372     # EAX = next-stream-line-equal?(_test-stream, "Abc")
373     # . . push args
374     68/push  "Abc"/imm32
375     68/push  _test-stream/imm32
376     # . . call
377     e8/call  next-stream-line-equal?/disp32
378     # . . discard args
379     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
380     # check-ints-equal(EAX, 1, msg)
381     # . . push args
382     68/push  "F - test-next-stream-line-equal-stops-at-newline"/imm32
383     68/push  1/imm32
384     50/push-EAX
385     # . . call
386     e8/call  check-ints-equal/disp32
387     # . . discard args
388     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
389     # . epilog
390     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
391     5d/pop-to-EBP
392     c3/return
393 
394 test-next-stream-line-equal-stops-at-newline-2:
395     # . prolog
396     55/push-EBP
397     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
398     # clear-stream(_test-stream)
399     # . . push args
400     68/push  _test-stream/imm32
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     # write(_test-stream, "Abc\ndef")
406     # . write(_test-stream, "Abc")
407     # . . push args
408     68/push  "Abc"/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, "def")
423     # . . push args
424     68/push  "def"/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     # EAX = next-stream-line-equal?(_test-stream, "def")
431     # . . push args
432     68/push  "def"/imm32
433     68/push  _test-stream/imm32
434     # . . call
435     e8/call  next-stream-line-equal?/disp32
436     # . . discard args
437     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
438     # check-ints-equal(EAX, 0, msg)
439     # . . push args
440     68/push  "F - test-next-stream-line-equal-stops-at-newline-2"/imm32
441     68/push  0/imm32
442     50/push-EAX
443     # . . call
444     e8/call  check-ints-equal/disp32
445     # . . discard args
446     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
447     # . epilog
448     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
449     5d/pop-to-EBP
450     c3/return
451 
452 test-next-stream-line-equal-skips-newline:
453     # . prolog
454     55/push-EBP
455     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
456     # clear-stream(_test-stream)
457     # . . push args
458     68/push  _test-stream/imm32
459     # . . call
460     e8/call  clear-stream/disp32
461     # . . discard args
462     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
463     # write(_test-stream, "Abc\ndef\n")
464     # . write(_test-stream, "Abc")
465     # . . push args
466     68/push  "Abc"/imm32
467     68/push  _test-stream/imm32
468     # . . call
469     e8/call  write/disp32
470     # . . discard args
471     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
472     # . write(_test-stream, Newline)
473     # . . push args
474     68/push  Newline/imm32
475     68/push  _test-stream/imm32
476     # . . call
477     e8/call  write/disp32
478     # . . discard args
479     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
480     # . write(_test-stream, "def")
481     # . . push args
482     68/push  "def"/imm32
483     68/push  _test-stream/imm32
484     # . . call
485     e8/call  write/disp32
486     # . . discard args
487     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
488     # . write(_test-stream, Newline)
489     # . . push args
490     68/push  Newline/imm32
491     68/push  _test-stream/imm32
492     # . . call
493     e8/call  write/disp32
494     # . . discard args
495     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
496     # next-stream-line-equal?(_test-stream, "Abc")
497     # . . push args
498     68/push  "Abc"/imm32
499     68/push  _test-stream/imm32
500     # . . call
501     e8/call  next-stream-line-equal?/disp32
502     # . . discard args
503     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
504     # EAX = next-stream-line-equal?(_test-stream, "def")
505     # . . push args
506     68/push  "def"/imm32
507     68/push  _test-stream/imm32
508     # . . call
509     e8/call  next-stream-line-equal?/disp32
510     # . . discard args
511     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
512     # check-ints-equal(EAX, 1, msg)
513     # . . push args
514     68/push  "F - test-next-stream-line-equal-skips-newline"/imm32
515     68/push  1/imm32
516     50/push-EAX
517     # . . call
518     e8/call  check-ints-equal/disp32
519     # . . discard args
520     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
521     # . epilog
522     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
523     5d/pop-to-EBP
524     c3/return
525 
526 test-next-stream-line-equal-handles-final-line:
527     # . prolog
528     55/push-EBP
529     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
530     # clear-stream(_test-stream)
531     # . . push args
532     68/push  _test-stream/imm32
533     # . . call
534     e8/call  clear-stream/disp32
535     # . . discard args
536     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
537     # write(_test-stream, "Abc\ndef")
538     # . write(_test-stream, "Abc")
539     # . . push args
540     68/push  "Abc"/imm32
541     68/push  _test-stream/imm32
542     # . . call
543     e8/call  write/disp32
544     # . . discard args
545     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
546     # . write(_test-stream, Newline)
547     # . . push args
548     68/push  Newline/imm32
549     68/push  _test-stream/imm32
550     # . . call
551     e8/call  write/disp32
552     # . . discard args
553     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
554     # . write(_test-stream, "def")
555     # . . push args
556     68/push  "def"/imm32
557     68/push  _test-stream/imm32
558     # . . call
559     e8/call  write/disp32
560     # . . discard args
561     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
562     # next-stream-line-equal?(_test-stream, "Abc")
563     # . . push args
564     68/push  "Abc"/imm32
565     68/push  _test-stream/imm32
566     # . . call
567     e8/call  next-stream-line-equal?/disp32
568     # . . discard args
569     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
570     # EAX = next-stream-line-equal?(_test-stream, "def")
571     # . . push args
572     68/push  "def"/imm32
573     68/push  _test-stream/imm32
574     # . . call
575     e8/call  next-stream-line-equal?/disp32
576     # . . discard args
577     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
578     # check-ints-equal(EAX, 1, msg)
579     # . . push args
580     68/push  "F - test-next-stream-line-equal-skips-newline"/imm32
581     68/push  1/imm32
582     50/push-EAX
583     # . . call
584     e8/call  check-ints-equal/disp32
585     # . . discard args
586     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
587     # . epilog
588     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
589     5d/pop-to-EBP
590     c3/return
591 
592 test-next-stream-line-equal-always-fails-after-eof:
593     # . prolog
594     55/push-EBP
595     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
596     # clear-stream(_test-stream)
597     # . . push args
598     68/push  _test-stream/imm32
599     # . . call
600     e8/call  clear-stream/disp32
601     # . . discard args
602     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
603     # write nothing
604     # EAX = next-stream-line-equal?(_test-stream, "")
605     # . . push args
606     68/push  ""/imm32
607     68/push  _test-stream/imm32
608     # . . call
609     e8/call  next-stream-line-equal?/disp32
610     # . . discard args
611     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
612     # check-ints-equal(EAX, 0, msg)
613     # . . push args
614     68/push  "F - test-next-stream-line-equal-always-fails-after-eof"/imm32
615     68/push  1/imm32
616     50/push-EAX
617     # . . call
618     e8/call  check-ints-equal/disp32
619     # . . discard args
620     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
621     # EAX = next-stream-line-equal?(_test-stream, "")
622     # . . push args
623     68/push  ""/imm32
624     68/push  _test-stream/imm32
625     # . . call
626     e8/call  next-stream-line-equal?/disp32
627     # . . discard args
628     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
629     # check-ints-equal(EAX, 0, msg)
630     # . . push args
631     68/push  "F - test-next-stream-line-equal-always-fails-after-eof/2"/imm32
632     68/push  1/imm32
633     50/push-EAX
634     # . . call
635     e8/call  check-ints-equal/disp32
636     # . . discard args
637     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
638     # . epilog
639     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
640     5d/pop-to-EBP
641     c3/return
642 
643 # helper for later tests
644 check-next-stream-line-equal:
645     # . prolog
646     55/push-EBP
647     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
648     # . save registers
649     50/push-EAX
650     # EAX = next-stream-line-equal?(f, s)
651     # . . push args
652     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
653     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
654     # . . call
655     e8/call  next-stream-line-equal?/disp32
656     # . . discard args
657     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
658     # check-ints-equal(EAX, 1, msg)
659     # . . push args
660     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
661     68/push  1/imm32
662     50/push-EAX
663     # . . call
664     e8/call  check-ints-equal/disp32
665     # . . discard args
666     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
667     # . restore registers
668     58/pop-to-EAX
669     # . epilog
670     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
671     5d/pop-to-EBP
672     c3/return