1 # some primitives for checking stream contents
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
8 # compare all the data in a stream (ignoring the read pointer)
9 stream-data-equal?: # f: (addr stream byte), s: (addr array byte) -> result/eax: boolean
12 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
19 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi
21 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax
22 # var maxf/edx: (addr byte) = &f->data[f->write]
23 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
24 # var currf/esi: (addr byte) = f->data
25 81 0/subop/add 3/mod/direct 6/rm32/esi . . . . . 0xc/imm32 # add to esi
27 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi
28 $stream-data-equal?:compare-sizes:
29 # if (f->write != s->size) return false
30 39/compare 0/mod/indirect 7/rm32/edi . . . 0/r32/eax . . # compare *edi and eax
31 75/jump-if-!= $stream-data-equal?:false/disp8
32 # var currs/edi: (addr byte) = s->data
33 81 0/subop/add 3/mod/direct 7/rm32/edi . . . . . 4/imm32 # add to edi
35 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax
37 31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx
38 $stream-data-equal?:loop:
39 # if (currf >= maxf) return true
40 39/compare 3/mod/direct 6/rm32/esi . . . 2/r32/edx . . # compare esi with edx
41 73/jump-if-addr>= $stream-data-equal?:true/disp8
43 8a/copy-byte 0/mod/indirect 6/rm32/esi . . . 0/r32/AL . . # copy byte at *esi to AL
45 8a/copy-byte 0/mod/indirect 7/rm32/edi . . . 1/r32/CL . . # copy byte at *edi to CL
46 # if (eax != ecx) return false
47 39/compare 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # compare eax and ecx
48 75/jump-if-!= $stream-data-equal?:false/disp8
53 eb/jump $stream-data-equal?:loop/disp8
54 $stream-data-equal?:false:
55 b8/copy-to-eax 0/imm32
56 eb/jump $stream-data-equal?:end/disp8
57 $stream-data-equal?:true:
58 b8/copy-to-eax 1/imm32
59 $stream-data-equal?:end:
66 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
70 test-stream-data-equal:
73 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
74 # clear-stream(_test-stream)
76 68/push _test-stream/imm32
78 e8/call clear-stream/disp32
80 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
81 # write(_test-stream, "Abc")
84 68/push _test-stream/imm32
88 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
89 # eax = stream-data-equal?(_test-stream, "Abc")
92 68/push _test-stream/imm32
94 e8/call stream-data-equal?/disp32
96 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
97 # check-ints-equal(eax, 1, msg)
99 68/push "F - test-stream-data-equal"/imm32
103 e8/call check-ints-equal/disp32
105 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
107 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
111 test-stream-data-equal-2:
114 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
115 # clear-stream(_test-stream)
117 68/push _test-stream/imm32
119 e8/call clear-stream/disp32
121 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
122 # write(_test-stream, "Abc")
125 68/push _test-stream/imm32
129 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
130 # eax = stream-data-equal?(_test-stream, "Abd")
133 68/push _test-stream/imm32
135 e8/call stream-data-equal?/disp32
137 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
138 # check-ints-equal(eax, 0, msg)
140 68/push "F - test-stream-data-equal-2"/imm32
144 e8/call check-ints-equal/disp32
146 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
148 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
152 test-stream-data-equal-size-check:
155 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
156 # clear-stream(_test-stream)
158 68/push _test-stream/imm32
160 e8/call clear-stream/disp32
162 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
163 # write(_test-stream, "Abc")
166 68/push _test-stream/imm32
170 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
171 # eax = stream-data-equal?(_test-stream, "Abcd")
174 68/push _test-stream/imm32
176 e8/call stream-data-equal?/disp32
178 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
179 # check-ints-equal(eax, 0, msg)
181 68/push "F - test-stream-data-equal-size-check"/imm32
185 e8/call check-ints-equal/disp32
187 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
189 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
194 check-stream-equal: # f: (addr stream byte), s: (addr array byte), msg: (addr array byte)
197 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
200 # eax = stream-data-equal?(f, s)
202 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
203 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
205 e8/call stream-data-equal?/disp32
207 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
208 # check-ints-equal(eax, 1, msg)
210 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
214 e8/call check-ints-equal/disp32
216 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
217 $check-stream-equal:end:
218 # . restore registers
221 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
225 # scan the next line until newline starting from f->read and compare it with
226 # 's' (ignoring the trailing newline)
227 # on success, set f->read to after the next newline
228 # on failure, leave f->read unmodified
229 # this function is usually used only in tests, so we repeatedly write f->read
230 next-stream-line-equal?: # f: (addr stream byte), s: (addr array byte) -> result/eax: boolean
232 # currf = f->read # bound: f->write
233 # currs = 0 # bound: s->size
235 # if currf >= f->write
236 # return currs >= s->size
237 # if f[currf] == '\n'
239 # return currs >= s->size
240 # if (currs >= s->size) return false # the current line of f still has data to match
241 # if (f[currf] != s[currs]) return false
245 # collapsing the two branches that can return true:
246 # currf = f->read # bound: f->write
247 # currs = 0 # bound: s->size
249 # if (currf >= f->write) break
250 # if (f[currf] == '\n') break
251 # if (currs >= s->size) return false # the current line of f still has data to match
252 # if (f[currf] != s[currs]) return false
255 # ++currf # skip '\n'
256 # return currs >= s->size
257 # Here the final `++currf` is sometimes unnecessary (if we're already at the end of the stream)
269 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
276 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi
277 # var currf/ecx: int = f->read
278 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx
280 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi
281 # var currs/edx: int = 0
282 31/xor 3/mod/direct 2/rm32/edx . . . 2/r32/edx . . # clear edx
283 # var c1/eax: byte = 0
284 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax
285 # var c2/ebx: byte = 0
286 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx
287 $next-stream-line-equal?:loop:
288 # if (currf >= f->write) break
289 3b/compare 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # compare ecx with *esi
290 7d/jump-if->= $next-stream-line-equal?:break/disp8
291 # c1 = f->data[f->read]
292 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
293 # if (c1 == '\n') break
294 3d/compare-eax-and 0xa/imm32/newline
295 74/jump-if-= $next-stream-line-equal?:break/disp8
296 # if (currs >= s->size) return false
297 3b/compare 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # compare edx with *edi
298 7d/jump-if->= $next-stream-line-equal?:false/disp8
299 # c2 = s->data[currs]
300 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
301 # if (c1 != c2) return false
302 39/compare 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # compare eax and ebx
303 75/jump-if-!= $next-stream-line-equal?:false/disp8
308 eb/jump $next-stream-line-equal?:loop/disp8
309 $next-stream-line-equal?:break:
312 # if (currs >= s->size) return true
313 3b/compare 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # compare edx with *edi
314 7c/jump-if-< $next-stream-line-equal?:false/disp8
315 $next-stream-line-equal?:true:
316 b8/copy-to-eax 1/imm32
317 # persist f->read on success
318 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy ecx to *(esi+4)
319 eb/jump $next-stream-line-equal?:end/disp8
320 $next-stream-line-equal?:false:
321 b8/copy-to-eax 0/imm32
322 $next-stream-line-equal?:end:
323 # . restore registers
329 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
333 test-next-stream-line-equal-stops-at-newline:
336 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
337 # clear-stream(_test-stream)
339 68/push _test-stream/imm32
341 e8/call clear-stream/disp32
343 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
344 # write(_test-stream, "Abc\ndef")
346 68/push "Abc\ndef"/imm32
347 68/push _test-stream/imm32
351 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
352 # eax = next-stream-line-equal?(_test-stream, "Abc")
355 68/push _test-stream/imm32
357 e8/call next-stream-line-equal?/disp32
359 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
360 # check-ints-equal(eax, 1, msg)
362 68/push "F - test-next-stream-line-equal-stops-at-newline"/imm32
366 e8/call check-ints-equal/disp32
368 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
370 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
374 test-next-stream-line-equal-stops-at-newline-2:
377 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
378 # clear-stream(_test-stream)
380 68/push _test-stream/imm32
382 e8/call clear-stream/disp32
384 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
385 # write(_test-stream, "Abc\ndef")
387 68/push "Abc\ndef"/imm32
388 68/push _test-stream/imm32
392 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
393 # eax = next-stream-line-equal?(_test-stream, "def")
396 68/push _test-stream/imm32
398 e8/call next-stream-line-equal?/disp32
400 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
401 # check-ints-equal(eax, 0, msg)
403 68/push "F - test-next-stream-line-equal-stops-at-newline-2"/imm32
407 e8/call check-ints-equal/disp32
409 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
411 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
415 test-next-stream-line-equal-skips-newline:
418 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
419 # clear-stream(_test-stream)
421 68/push _test-stream/imm32
423 e8/call clear-stream/disp32
425 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
426 # write(_test-stream, "Abc\ndef\n")
428 68/push "Abc\ndef\n"/imm32
429 68/push _test-stream/imm32
433 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
434 # next-stream-line-equal?(_test-stream, "Abc")
437 68/push _test-stream/imm32
439 e8/call next-stream-line-equal?/disp32
441 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
442 # eax = next-stream-line-equal?(_test-stream, "def")
445 68/push _test-stream/imm32
447 e8/call next-stream-line-equal?/disp32
449 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
450 # check-ints-equal(eax, 1, msg)
452 68/push "F - test-next-stream-line-equal-skips-newline"/imm32
456 e8/call check-ints-equal/disp32
458 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
460 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
464 test-next-stream-line-equal-handles-final-line:
467 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
468 # clear-stream(_test-stream)
470 68/push _test-stream/imm32
472 e8/call clear-stream/disp32
474 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
475 # write(_test-stream, "Abc\ndef")
477 68/push "Abc\ndef"/imm32
478 68/push _test-stream/imm32
482 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
483 # next-stream-line-equal?(_test-stream, "Abc")
486 68/push _test-stream/imm32
488 e8/call next-stream-line-equal?/disp32
490 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
491 # eax = next-stream-line-equal?(_test-stream, "def")
494 68/push _test-stream/imm32
496 e8/call next-stream-line-equal?/disp32
498 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
499 # check-ints-equal(eax, 1, msg)
501 68/push "F - test-next-stream-line-equal-skips-newline"/imm32
505 e8/call check-ints-equal/disp32
507 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
509 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
513 test-next-stream-line-equal-always-fails-after-Eof:
516 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
517 # clear-stream(_test-stream)
519 68/push _test-stream/imm32
521 e8/call clear-stream/disp32
523 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
525 # eax = next-stream-line-equal?(_test-stream, "")
528 68/push _test-stream/imm32
530 e8/call next-stream-line-equal?/disp32
532 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
533 # check-ints-equal(eax, 0, msg)
535 68/push "F - test-next-stream-line-equal-always-fails-after-Eof"/imm32
539 e8/call check-ints-equal/disp32
541 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
542 # eax = next-stream-line-equal?(_test-stream, "")
545 68/push _test-stream/imm32
547 e8/call next-stream-line-equal?/disp32
549 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
550 # check-ints-equal(eax, 0, msg)
552 68/push "F - test-next-stream-line-equal-always-fails-after-Eof/2"/imm32
556 e8/call check-ints-equal/disp32
558 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
560 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
564 # helper for later tests
565 check-next-stream-line-equal: # f: (addr stream byte), s: (addr array byte), msg: (addr array byte)
568 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
571 # eax = next-stream-line-equal?(f, s)
573 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
574 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
576 e8/call next-stream-line-equal?/disp32
578 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
579 # check-ints-equal(eax, 1, msg)
581 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
585 e8/call check-ints-equal/disp32
587 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
588 # . restore registers
591 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
595 # . . vim:nowrap:textwidth=0