From 3350c34a74844e21ea69077e01efff3bae64bdcd Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Tue, 23 Mar 2021 17:31:08 -0700 Subject: . --- html/linux/107trace.subx.html | 1054 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1054 insertions(+) create mode 100644 html/linux/107trace.subx.html (limited to 'html/linux/107trace.subx.html') diff --git a/html/linux/107trace.subx.html b/html/linux/107trace.subx.html new file mode 100644 index 00000000..da49bc51 --- /dev/null +++ b/html/linux/107trace.subx.html @@ -0,0 +1,1054 @@ + + + + +Mu - linux/107trace.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/107trace.subx +
+  1 # primitives for emitting traces to a 'trace' stream, and for tests to make assertions on its contents
+  2 #
+  3 # A trace stream looks like a regular stream:
+  4 #   write: int  # index at which writes go
+  5 #   read: int  # index that we've read until
+  6 #   data: (array byte)  # prefixed by size as usual
+  7 # Usually the trace stream will be in a separate segment set aside for the purpose.
+  8 #
+  9 # primitives for operating on traces (arguments in quotes):
+ 10 #   - initialize-trace-stream: populates Trace-stream with a new segment of the given 'size'
+ 11 #   - trace: adds a 'line' to Trace-stream
+ 12 #   - check-trace-contains: scans from Trace-stream's start for a matching 'line', prints a 'message' to stderr on failure
+ 13 #   - check-trace-scans-to: scans from Trace-stream's read pointer for a matching 'line', prints a 'message' to stderr on failure
+ 14 
+ 15 == data
+ 16 
+ 17 Trace-stream:  # (addr stream byte)  # TODO: make this a handle
+ 18     0/imm32
+ 19 
+ 20 Trace-segment:
+ 21     0/imm32/curr
+ 22     0/imm32/limit
+ 23 
+ 24 # Fake trace-stream for tests.
+ 25 # Also illustrates the layout of the real trace-stream (segment).
+ 26 _test-trace-stream:  # (stream byte)
+ 27     # current write index
+ 28     0/imm32
+ 29     # current read index
+ 30     0/imm32
+ 31     # size
+ 32     8/imm32
+ 33     # data
+ 34     00 00 00 00 00 00 00 00  # 8 bytes
+ 35 
+ 36 == code
+ 37 #   instruction                     effective address                                                   register    displacement    immediate
+ 38 # . op          subop               mod             rm32          base        index         scale       r32
+ 39 # . 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
+ 40 
+ 41 # Allocate a new segment for the trace stream, initialize its size, and save its address to Trace-stream.
+ 42 # The Trace-stream segment will consist of variable-length lines separated by newlines (0x0a)
+ 43 initialize-trace-stream:  # n: int
+ 44     # . prologue
+ 45     55/push-ebp
+ 46     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+ 47     # . save registers
+ 48     50/push-eax
+ 49     51/push-ecx
+ 50     # ecx = n
+ 51     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
+ 52     # Trace-segment = new-segment(n)
+ 53     # . . push args
+ 54     68/push  Trace-segment/imm32
+ 55     51/push-ecx
+ 56     # . . call
+ 57     e8/call  new-segment/disp32
+ 58     # . . discard args
+ 59     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 60     # copy Trace-segment->curr to *Trace-stream
+ 61     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-segment/disp32              # copy *Trace-segment to eax
+ 62 #?     # watch point to catch Trace-stream leaks
+ 63 #? $watch-1:
+ 64     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy eax to *Trace-stream
+ 65     # Trace-stream->size = n - 12
+ 66     # . ecx -= 12
+ 67     81          5/subop/subtract    3/mod/direct    1/rm32/ecx    .           .             .           .           .               0xc/imm32         # subtract from ecx
+ 68     # . Trace-stream->size = ecx
+ 69     89/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           1/r32/ecx   8/disp8         .                 # copy ecx to *(eax+8)
+ 70 $initialize-trace-stream:end:
+ 71     # . restore registers
+ 72     59/pop-to-ecx
+ 73     58/pop-to-eax
+ 74     # . epilogue
+ 75     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+ 76     5d/pop-to-ebp
+ 77     c3/return
+ 78 
+ 79 # Append a string to the given trace stream.
+ 80 # Silently give up if it's already full. Or truncate the string if there isn't enough room.
+ 81 trace:  # line: (addr array byte)
+ 82     # . prologue
+ 83     55/push-ebp
+ 84     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+ 85     # . save registers
+ 86     50/push-eax
+ 87     51/push-ecx
+ 88     52/push-edx
+ 89     53/push-ebx
+ 90     56/push-esi
+ 91     57/push-edi
+ 92     # var edi: (addr stream byte) = *Trace-stream
+ 93     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           7/r32/edi   Trace-stream/disp32               # copy *Trace-stream to edi
+ 94     # esi = line
+ 95     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
+ 96     # var ecx: int = t->write
+ 97     8b/copy                         0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # copy *edi to ecx
+ 98     # var edx: int = t->size
+ 99     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           2/r32/edx   8/disp8         .                 # copy *(edi+8) to edx
+100     # eax = _append-3(&t->data[t->write], &t->data[t->size], line)
+101     # . . push line
+102     56/push-esi
+103     # . . push &t->data[t->size]
+104     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/edi  2/index/edx   .           3/r32/ebx   0xc/disp8       .                 # copy edi+edx+12 to ebx
+105     53/push-ebx
+106     # . . push &t->data[t->write]
+107     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/edi  1/index/ecx   .           3/r32/ebx   0xc/disp8       .                 # copy edi+ecx+12 to ebx
+108     53/push-ebx
+109     # . . call
+110     e8/call  _append-3/disp32
+111     # . . discard args
+112     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+113     # if (eax == 0) return
+114     3d/compare-eax-and  0/imm32
+115     74/jump-if-=  $trace:end/disp8
+116     # t->write += eax
+117     01/add                          0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # add eax to *edi
+118     # refresh ecx = t->write
+119     8b/copy                         0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # copy *edi to ecx
+120     # eax = _append-3(&t->data[t->write], &t->data[t->size], line)
+121     # . . push line
+122     68/push  Newline/imm32
+123     # . . push &t->data[t->size]
+124     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/edi  2/index/edx   .           3/r32/ebx   0xc/disp8       .                 # copy edi+edx+12 to ebx
+125     53/push-ebx
+126     # . . push &t->data[t->write]
+127     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/edi  1/index/ecx   .           3/r32/ebx   0xc/disp8       .                 # copy edi+ecx+12 to ebx
+128     53/push-ebx
+129     # . . call
+130     e8/call  _append-3/disp32
+131     # . . discard args
+132     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+133     # t->write += eax
+134     01/add                          0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # add eax to *edi
+135 $trace:end:
+136     # . restore registers
+137     5f/pop-to-edi
+138     5e/pop-to-esi
+139     5b/pop-to-ebx
+140     5a/pop-to-edx
+141     59/pop-to-ecx
+142     58/pop-to-eax
+143     # . epilogue
+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 test-trace-single:
+149     # push *Trace-stream
+150     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+151     # *Trace-stream = _test-trace-stream
+152     b8/copy-to-eax  _test-trace-stream/imm32
+153     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy eax to *Trace-stream
+154     # clear-trace-stream()
+155     e8/call  clear-trace-stream/disp32
+156     # trace("Ab")
+157     # . . push args
+158     68/push  "Ab"/imm32
+159     # . . call
+160     e8/call  trace/disp32
+161     # . . discard args
+162     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+163     # check-ints-equal(*_test-trace-stream->data, 41/A 62/b 0a/newline 00, msg)
+164     # . . push args
+165     68/push  "F - test-trace-single"/imm32
+166     68/push  0x0a6241/imm32/Ab-newline
+167     # . . push *_test-trace-stream->data
+168     b8/copy-to-eax  _test-trace-stream/imm32
+169     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           0xc/disp8       .                 # push *(eax+12)
+170     # . . call
+171     e8/call  check-ints-equal/disp32
+172     # . . discard args
+173     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+174     # pop into *Trace-stream
+175     8f          0/subop/pop         0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # pop into *Trace-stream
+176     # end
+177     c3/return
+178 
+179 test-trace-appends:
+180     # push *Trace-stream
+181     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+182     # *Trace-stream = _test-trace-stream
+183     b8/copy-to-eax  _test-trace-stream/imm32
+184     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy eax to *Trace-stream
+185     # clear-trace-stream()
+186     e8/call  clear-trace-stream/disp32
+187     # trace("C")
+188     # . . push args
+189     68/push  "C"/imm32
+190     # . . call
+191     e8/call  trace/disp32
+192     # . . discard args
+193     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+194     # trace("D")
+195     # . . push args
+196     68/push  "D"/imm32
+197     # . . call
+198     e8/call  trace/disp32
+199     # . . discard args
+200     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+201     # check-ints-equal(*_test-trace-stream->data, 43/C 0a/newline 44/D 0a/newline, msg)
+202     # . . push args
+203     68/push  "F - test-trace-appends"/imm32
+204     68/push  0x0a440a43/imm32/C-newline-D-newline
+205     # . . push *_test-trace-stream->data
+206     b8/copy-to-eax  _test-trace-stream/imm32
+207     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           0xc/disp8       .                 # push *(eax+12)
+208     # . . call
+209     e8/call  check-ints-equal/disp32
+210     # . . discard args
+211     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+212     # pop into *Trace-stream
+213     8f          0/subop/pop         0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # pop into *Trace-stream
+214     # end
+215     c3/return
+216 
+217 test-trace-empty-line:
+218     # push *Trace-stream
+219     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+220     # *Trace-stream = _test-trace-stream
+221     b8/copy-to-eax  _test-trace-stream/imm32
+222     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy eax to *Trace-stream
+223     # clear-trace-stream()
+224     e8/call  clear-trace-stream/disp32
+225     # trace("")
+226     # . . push args
+227     68/push  ""/imm32
+228     # . . call
+229     e8/call  trace/disp32
+230     # . . discard args
+231     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+232     # check-ints-equal(*_test-trace-stream->data, 0, msg)
+233     # . . push args
+234     68/push  "F - test-trace-empty-line"/imm32
+235     68/push  0/imm32
+236     # . . push *_test-trace-stream->data
+237     b8/copy-to-eax  _test-trace-stream/imm32
+238     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           0xc/disp8       .                 # push *(eax+12)
+239     # . . call
+240     e8/call  check-ints-equal/disp32
+241     # . . discard args
+242     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+243     # pop into *Trace-stream
+244     8f          0/subop/pop         0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # pop into *Trace-stream
+245     # end
+246     c3/return
+247 
+248 check-trace-contains:  # line: (addr string), msg: (addr string)
+249     # . prologue
+250     55/push-ebp
+251     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+252     # rewind-stream(*Trace-stream)
+253     # . . push args
+254     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+255     # . . call
+256     e8/call  rewind-stream/disp32
+257     # . . discard args
+258     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+259     # check-trace-scans-to(line, msg)
+260     # . . push args
+261     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+262     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+263     # . . call
+264     e8/call  check-trace-scans-to/disp32
+265     # . . discard args
+266     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+267 $check-trace-contains:end:
+268     # . epilogue
+269     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+270     5d/pop-to-ebp
+271     c3/return
+272 
+273 check-trace-scans-to:  # line: (addr string), msg: (addr string)
+274     # . prologue
+275     55/push-ebp
+276     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+277     # . save registers
+278     50/push-eax
+279     # eax = trace-scan(line)
+280     # . . push args
+281     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+282     # . . call
+283     e8/call  trace-scan/disp32
+284     # . . discard args
+285     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+286     # check-ints-equal(eax, 1, msg)
+287     # . . push args
+288     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+289     68/push  1/imm32
+290     50/push-eax
+291     # . . call
+292     e8/call check-ints-equal/disp32
+293     # . . discard args
+294     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+295 $check-trace-scans-to:end:
+296     # . restore registers
+297     58/pop-to-eax
+298     # . epilogue
+299     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+300     5d/pop-to-ebp
+301     c3/return
+302 
+303 # Start scanning from Trace-stream->read for 'line'. If found, update Trace-stream->read and return true.
+304 trace-scan:  # line: (addr array byte) -> result/eax: boolean
+305     # pseudocode:
+306     #   push Trace-stream->read
+307     #   while true:
+308     #     if Trace-stream->read >= Trace-stream->write
+309     #       break
+310     #     if next-line-matches?(Trace-stream, line)
+311     #       skip-next-line(Trace-stream)
+312     #       dump saved copy of Trace-stream->read
+313     #       return true
+314     #     skip-next-line(Trace-stream)
+315     #   pop saved copy of Trace-stream->read
+316     #   return false
+317     #
+318     # . prologue
+319     55/push-ebp
+320     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+321     # . save registers
+322     51/push-ecx
+323     56/push-esi
+324     # esi = *Trace-stream
+325     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           6/r32/esi   Trace-stream/disp32               # copy *Trace-stream to esi
+326     # ecx = Trace-stream->write
+327     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx                   .                 # copy *esi to ecx
+328     # push Trace-stream->read
+329     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
+330 $trace-scan:loop:
+331     # if (Trace-stream->read >= Trace-stream->write) return false
+332     39/compare                      1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # compare ecx with *(esi+4)
+333     7d/jump-if->=  $trace-scan:false/disp8
+334     # eax = next-line-matches?(Trace-stream, line)
+335     # . . push args
+336     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+337     56/push-esi
+338     # . . call
+339     e8/call  next-line-matches?/disp32
+340     # . . discard args
+341     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+342     # if (eax == false) continue
+343     3d/compare-eax-and  0/imm32/false
+344     74/jump-if-=  $trace-scan:continue/disp8
+345 $trace-scan:true:
+346     # skip-next-line(Trace-stream)
+347     # . . push args
+348     56/push-esi
+349     # . . call
+350     e8/call  skip-next-line/disp32
+351     # . . discard args
+352     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+353     # dump saved copy of Trace-stream->read
+354     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+355     # return true
+356     b8/copy-to-eax  1/imm32/true
+357     eb/jump  $trace-scan:end/disp8
+358 $trace-scan:continue:
+359     # skip-next-line(Trace-stream)
+360     # . . push args
+361     56/push-esi
+362     # . . call
+363     e8/call  skip-next-line/disp32
+364     # . . discard args
+365     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+366     eb/jump  $trace-scan:loop/disp8
+367 $trace-scan:false:
+368     # restore saved copy of Trace-stream->read
+369     8f          0/subop/pop         1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # pop to *(esi+4)
+370     # return false
+371     b8/copy-to-eax  0/imm32/false
+372 $trace-scan:end:
+373     # . restore registers
+374     59/pop-to-ecx
+375     # . epilogue
+376     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+377     5d/pop-to-ebp
+378     c3/return
+379 
+380 test-trace-scan-first:
+381     # push *Trace-stream
+382     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+383     # setup
+384     # . *Trace-stream = _test-trace-stream
+385     b8/copy-to-eax  _test-trace-stream/imm32
+386     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy eax to *Trace-stream
+387     # . clear-trace-stream()
+388     e8/call  clear-trace-stream/disp32
+389     # . trace("Ab")
+390     # . . push args
+391     68/push  "Ab"/imm32
+392     # . . call
+393     e8/call  trace/disp32
+394     # . . discard args
+395     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+396     # eax = trace-scan("Ab")
+397     # . . push args
+398     68/push  "Ab"/imm32
+399     # . . call
+400     e8/call  trace-scan/disp32
+401     # . . discard args
+402     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+403     # check-ints-equal(eax, 1, msg)
+404     # . . push args
+405     68/push  "F - test-trace-scan-first"/imm32
+406     68/push  1/imm32
+407     50/push-eax
+408     # . . call
+409     e8/call check-ints-equal/disp32
+410     # . . discard args
+411     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+412     # pop into *Trace-stream
+413     8f          0/subop/pop         0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # pop into *Trace-stream
+414     # . end
+415     c3/return
+416 
+417 test-trace-scan-skips-lines-until-found:
+418     # push *Trace-stream
+419     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+420     # setup
+421     # . *Trace-stream = _test-trace-stream
+422     b8/copy-to-eax  _test-trace-stream/imm32
+423     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy eax to *Trace-stream
+424     # . clear-trace-stream()
+425     e8/call  clear-trace-stream/disp32
+426     # . trace("Ab")
+427     # . . push args
+428     68/push  "Ab"/imm32
+429     # . . call
+430     e8/call  trace/disp32
+431     # . . discard args
+432     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+433     # . trace("cd")
+434     # . . push args
+435     68/push  "cd"/imm32
+436     # . . call
+437     e8/call  trace/disp32
+438     # . . discard args
+439     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+440     # eax = trace-scan("cd")
+441     # . . push args
+442     68/push  "cd"/imm32
+443     # . . call
+444     e8/call  trace-scan/disp32
+445     # . . discard args
+446     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+447     # check-ints-equal(eax, 1, msg)
+448     # . . push args
+449     68/push  "F - test-trace-scan-skips-lines-until-found"/imm32
+450     68/push  1/imm32
+451     50/push-eax
+452     # . . call
+453     e8/call check-ints-equal/disp32
+454     # . . discard args
+455     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+456     # pop into *Trace-stream
+457     8f          0/subop/pop         0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # pop into *Trace-stream
+458     # . end
+459     c3/return
+460 
+461 test-trace-second-scan-starts-where-first-left-off:
+462     # push *Trace-stream
+463     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+464     # setup
+465     # . *Trace-stream = _test-trace-stream
+466     b8/copy-to-eax  _test-trace-stream/imm32
+467     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy eax to *Trace-stream
+468     # . clear-trace-stream()
+469     e8/call  clear-trace-stream/disp32
+470     # . trace("Ab")
+471     # . . push args
+472     68/push  "Ab"/imm32
+473     # . . call
+474     e8/call  trace/disp32
+475     # . . discard args
+476     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+477     # . eax = trace-scan("Ab")
+478     # . . push args
+479     68/push  "Ab"/imm32
+480     # . . call
+481     e8/call  trace-scan/disp32
+482     # . . discard args
+483     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+484     # second scan fails
+485     # . eax = trace-scan("Ab")
+486     # . . push args
+487     68/push  "Ab"/imm32
+488     # . . call
+489     e8/call  trace-scan/disp32
+490     # . . discard args
+491     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+492     # check-ints-equal(eax, 0, msg)
+493     # . . push args
+494     68/push  "F - test-trace-second-scan-starts-where-first-left-off"/imm32
+495     68/push  0/imm32
+496     50/push-eax
+497     # . . call
+498     e8/call check-ints-equal/disp32
+499     # . . discard args
+500     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+501     # pop into *Trace-stream
+502     8f          0/subop/pop         0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # pop into *Trace-stream
+503     # . end
+504     c3/return
+505 
+506 test-trace-scan-failure-leaves-read-index-untouched:
+507     # push *Trace-stream
+508     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+509     # setup
+510     # . *Trace-stream = _test-trace-stream
+511     b8/copy-to-eax  _test-trace-stream/imm32
+512     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy eax to *Trace-stream
+513     # . clear-trace-stream()
+514     e8/call  clear-trace-stream/disp32
+515     # . trace("Ab")
+516     # . . push args
+517     68/push  "Ab"/imm32
+518     # . . call
+519     e8/call  trace/disp32
+520     # . . discard args
+521     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+522     # . check-ints-equal(_test-trace-stream->read, 0, msg)
+523     # . . push args
+524     68/push  "F - test-trace-scan-failure-leaves-read-index-untouched/precondition-failure"/imm32
+525     68/push  0/imm32
+526     b8/copy-to-eax  _test-trace-stream/imm32
+527     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
+528     # . . call
+529     e8/call check-ints-equal/disp32
+530     # . . discard args
+531     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+532     # perform a failing scan
+533     # . eax = trace-scan("Ax")
+534     # . . push args
+535     68/push  "Ax"/imm32
+536     # . . call
+537     e8/call  trace-scan/disp32
+538     # . . discard args
+539     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+540     # no change in read index
+541     # . check-ints-equal(_test-trace-stream->read, 0, msg)
+542     # . . push args
+543     68/push  "F - test-trace-scan-failure-leaves-read-index-untouched"/imm32
+544     68/push  0/imm32
+545     b8/copy-to-eax  _test-trace-stream/imm32
+546     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
+547     # . . call
+548     e8/call check-ints-equal/disp32
+549     # . . discard args
+550     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+551     # pop into *Trace-stream
+552     8f          0/subop/pop         0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # pop into *Trace-stream
+553     # . end
+554     c3/return
+555 
+556 next-line-matches?:  # t: (addr stream byte), line: (addr array byte) -> result/eax: boolean
+557     # pseudocode:
+558     #   while true:
+559     #     if (currl >= maxl) break
+560     #     if (currt >= maxt) return false
+561     #     if (*currt != *currl) return false
+562     #     ++currt
+563     #     ++currl
+564     #   return *currt == '\n'
+565     #
+566     # . prologue
+567     55/push-ebp
+568     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+569     # . save registers
+570     51/push-ecx
+571     52/push-edx
+572     53/push-ebx
+573     56/push-esi
+574     57/push-edi
+575     # edx = line
+576     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         2/r32/edx   0xc/disp8       .                 # copy *(ebp+12) to edx
+577     # var currl/esi: (addr byte) = line->data
+578     # . esi = line/edx->data
+579     8d/copy-address                 1/mod/*+disp8   2/rm32/edx    .           .             .           6/r32/esi   4/disp8         .                 # copy edx+4 to esi
+580     # var maxl/ecx: (addr byte) = &line->data[line->size]
+581     # . eax = line/edx->size
+582     8b/copy                         0/mod/indirect  2/rm32/edx    .           .                         0/r32/eax   .               .                 # copy *edx to eax
+583     # . maxl = &line->data[line->size]
+584     8d/copy-address                 0/mod/indirect  4/rm32/sib    6/base/esi  0/index/eax   .           1/r32/ecx   .               .                 # copy edx+eax to ecx
+585     # edi = t
+586     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
+587     # var ebx: (addr byte) = t->data
+588     8d/copy-address                 1/mod/*+disp8   7/rm32/edi    .           .             .           3/r32/ebx   0xc/disp8       .                 # copy edi+12 to ebx
+589     # var maxt/edx: (addr byte) = &t->data[t->write]
+590     # . eax = t->write
+591     8b/copy                         0/mod/indirect  7/rm32/edi    .           .                         0/r32/eax   .               .                 # copy *edi to eax
+592     # . maxt = &t->data[t->write]
+593     8d/copy-address                 0/mod/indirect  4/rm32/sib    3/base/ebx  0/index/eax   .           2/r32/edx   .               .                 # copy ebx+eax to edx
+594     # var currt/edi: (addr byte) = &t->data[t->read]
+595     # . eax = t/edi->read
+596     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .                         0/r32/eax   4/disp8         .                 # copy *(edi+4) to eax
+597     # . currt = &t->data[t->read]
+598     8d/copy-address                 0/mod/indirect  4/rm32/sib    3/base/ebx  0/index/eax   .           7/r32/edi   .               .                 # copy ebx+eax to edi
+599 $next-line-matches?:loop:
+600     # if (currl >= maxl) break
+601     39/compare                      3/mod/direct    6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare esi and ecx
+602     73/jump-if-addr>=  $next-line-matches?:break/disp8
+603     # if (currt >= maxt) return false
+604     # . eax = false
+605     b8/copy-to-eax  0/imm32/false
+606     39/compare                      3/mod/direct    7/rm32/edi    .           .             .           2/r32/edx   .               .                 # compare edi and edx
+607     73/jump-if-addr>=  $next-line-matches?:end/disp8
+608     # if (*currt != *currl) return false
+609     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
+610     31/xor                          3/mod/direct    3/rm32/eax    .           .             .           3/r32/eax   .               .                 # clear ebx
+611     # . eax: byte = *currt
+612     8a/copy-byte                    0/mod/indirect  7/rm32/edi    .           .                         0/r32/eax   .               .                 # copy *edi to eax
+613     # . ebx: byte = *currl
+614     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .                         3/r32/ebx   .               .                 # copy *esi to ebx
+615     # . eax >= ebx
+616     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # compare eax and ebx
+617     # . eax = false
+618     b8/copy-to-eax  0/imm32/false
+619     75/jump-if-!=  $next-line-matches?:end/disp8
+620     # ++currt
+621     47/increment-edi
+622     # ++currl
+623     46/increment-esi
+624     eb/jump  $next-line-matches?:loop/disp8
+625 $next-line-matches?:break:
+626     # return *currt == '\n'
+627     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
+628     # . eax: byte = *currt
+629     8a/copy-byte                    0/mod/indirect  7/rm32/edi    .           .                         0/r32/eax   .               .                 # copy *edi to eax
+630     3d/compare-eax-and  0xa/imm32/newline
+631     # . eax = false
+632     b8/copy-to-eax  1/imm32/true
+633     74/jump-if-=  $next-line-matches?:end/disp8
+634     b8/copy-to-eax  0/imm32/true
+635 $next-line-matches?:end:
+636     # . restore registers
+637     5f/pop-to-edi
+638     5e/pop-to-esi
+639     5b/pop-to-ebx
+640     5a/pop-to-edx
+641     59/pop-to-ecx
+642     # . epilogue
+643     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+644     5d/pop-to-ebp
+645     c3/return
+646 
+647 test-next-line-matches?-no-match-1:
+648     # next line of "ABABA" does not match "blah blah"
+649     # . eax = next-line-matches?(_test-stream-line-ABABA, "blah blah")
+650     # . . push args
+651     68/push  "blah blah"/imm32
+652     68/push  _test-stream-line-ABABA/imm32
+653     # . . call
+654     e8/call  next-line-matches?/disp32
+655     # . . discard args
+656     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+657     # . check-ints-equal(eax, 0, msg)
+658     # . . push args
+659     68/push  "F - test-next-line-matches?-no-match-1"/imm32
+660     68/push  0/imm32
+661     50/push-eax
+662     # . . call
+663     e8/call  check-ints-equal/disp32
+664     # . . discard args
+665     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+666     c3/return
+667 
+668 test-next-line-matches?-no-match-2:
+669     # next line of "ABABA" does not match ""
+670     # . eax = next-line-matches?(_test-stream-line-ABABA, "")
+671     # . . push args
+672     68/push  ""/imm32
+673     68/push  _test-stream-line-ABABA/imm32
+674     # . . call
+675     e8/call  next-line-matches?/disp32
+676     # . . discard args
+677     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+678     # . check-ints-equal(eax, 0, msg)
+679     # . . push args
+680     68/push  "F - test-next-line-matches?-no-match-2"/imm32
+681     68/push  0/imm32
+682     50/push-eax
+683     # . . call
+684     e8/call  check-ints-equal/disp32
+685     # . . discard args
+686     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+687     c3/return
+688 
+689 test-next-line-matches?-no-match-3:
+690     # next line of "ABABA" does not match  "AA"
+691     # . eax = next-line-matches?(_test-stream-line-ABABA, "AA")
+692     # . . push args
+693     68/push  "AA"/imm32
+694     68/push  _test-stream-line-ABABA/imm32
+695     # . . call
+696     e8/call  next-line-matches?/disp32
+697     # . . discard args
+698     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+699     # . check-ints-equal(eax, 0, msg)
+700     # . . push args
+701     68/push  "F - test-next-line-matches?-no-match-3"/imm32
+702     68/push  0/imm32
+703     50/push-eax
+704     # . . call
+705     e8/call  check-ints-equal/disp32
+706     # . . discard args
+707     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+708     c3/return
+709 
+710 test-next-line-matches?-match:
+711     # next line of "ABABA" matches "ABABA"
+712     # . eax = next-line-matches?(_test-stream-line-ABABA, "ABABA")
+713     # . . push args
+714     68/push  "ABABA"/imm32
+715     68/push  _test-stream-line-ABABA/imm32
+716     # . . call
+717     e8/call  next-line-matches?/disp32
+718     # . . discard args
+719     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+720     # . check-ints-equal(eax, 1, msg)
+721     # . . push args
+722     68/push  "F - test-next-line-matches?-match"/imm32
+723     68/push  1/imm32
+724     50/push-eax
+725     # . . call
+726     e8/call  check-ints-equal/disp32
+727     # . . discard args
+728     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+729     c3/return
+730 
+731 # move t->read to _after_ next newline
+732 skip-next-line:  # t: (addr stream byte)
+733     # pseudocode:
+734     #   max = &t->data[t->write]
+735     #   i = t->read
+736     #   curr = &t->data[t->read]
+737     #   while true
+738     #     if (curr >= max) break
+739     #     ++i
+740     #     if (*curr == '\n') break
+741     #     ++curr
+742     #   t->read = i
+743     #
+744     # . prologue
+745     55/push-ebp
+746     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+747     # . save registers
+748     50/push-eax
+749     51/push-ecx
+750     52/push-edx
+751     53/push-ebx
+752     # ecx = t
+753     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
+754     # edx = t->data
+755     8d/copy-address                 1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   0xc/disp8       .                 # copy ecx+12 to edx
+756     # eax = t->write
+757     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
+758     # var max/ebx: (addr byte) = &t->data[t->write]
+759     8d/copy-address                 0/mod/indirect  4/rm32/sib    2/base/edx  0/index/eax   .           3/r32/ebx   .               .                 # copy edx+eax to ebx
+760     # eax = t->read
+761     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to edx
+762     # var curr/ecx: (addr byte) = &t->data[t->read]
+763     8d/copy-address                 0/mod/indirect  4/rm32/sib    2/base/edx  0/index/eax   .           1/r32/ecx   .               .                 # copy edx+eax to ecx
+764     # var i/edx: int = t->read
+765     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to edx
+766 $skip-next-line:loop:
+767     # if (curr >= max) break
+768     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           3/r32/ebx   .               .                 # compare ecx and ebx
+769     73/jump-if-addr>=  $skip-next-line:end/disp8
+770     # ++i
+771     42/increment-edx
+772     # if (*curr == '\n') break
+773     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
+774     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
+775     3d/compare-eax-and  0a/imm32/newline
+776     74/jump-if-=  $skip-next-line:end/disp8
+777     # ++curr
+778     41/increment-ecx
+779     # loop
+780     eb/jump  $skip-next-line:loop/disp8
+781 $skip-next-line:end:
+782     # ecx = t
+783     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
+784     # t->read = i
+785     89/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy edx to *(ecx+4)
+786     # . restore registers
+787     5b/pop-to-ebx
+788     5a/pop-to-edx
+789     59/pop-to-ecx
+790     58/pop-to-eax
+791     # . epilogue
+792     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+793     5d/pop-to-ebp
+794     c3/return
+795 
+796 test-skip-next-line-empty:
+797     # skipping next line in empty stream leaves read pointer at 0
+798     # . skip-next-line(_test-stream-empty)
+799     # . . push args
+800     68/push  _test-stream-empty/imm32
+801     # . . call
+802     e8/call  skip-next-line/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-stream-empty->read, 0, msg)
+806     # . . push args
+807     68/push  "F - test-skip-next-line-empty"/imm32
+808     68/push  0/imm32
+809     b8/copy-to-eax  _test-stream-empty/imm32
+810     8b/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           0/r32/eax   4/disp8         .                 # copy *(eax+4) to eax
+811     50/push-eax
+812     # . . call
+813     e8/call  check-ints-equal/disp32
+814     # . . discard args
+815     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+816     c3/return
+817 
+818 test-skip-next-line-filled:
+819     # skipping next line increments read pointer by length of line + 1 (for newline)
+820     # . skip-next-line(_test-stream-filled)
+821     # . . push args
+822     68/push  _test-stream-filled/imm32
+823     # . . call
+824     e8/call  skip-next-line/disp32
+825     # . . discard args
+826     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+827     # . check-ints-equal(_test-stream-filled->read, 5, msg)
+828     # . . push args
+829     68/push  "F - test-skip-next-line-filled"/imm32
+830     68/push  5/imm32
+831     b8/copy-to-eax  _test-stream-filled/imm32
+832     8b/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           0/r32/eax   4/disp8         .                 # copy *(eax+4) to eax
+833     50/push-eax
+834     # . . call
+835     e8/call  check-ints-equal/disp32
+836     # . . discard args
+837     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+838     c3/return
+839 
+840 clear-trace-stream:
+841     # . prologue
+842     55/push-ebp
+843     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+844     # clear-stream(*Trace-stream)
+845     # . . push args
+846     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+847     # . . call
+848     e8/call  clear-stream/disp32
+849     # . . discard args
+850     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+851 $clear-trace-stream:end:
+852     # . epilogue
+853     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+854     5d/pop-to-ebp
+855     c3/return
+856 
+857 # - helpers
+858 
+859 # 3-argument variant of _append
+860 _append-3:  # out: (addr byte), outend: (addr byte), s: (addr array byte) -> num_bytes_appended/eax
+861     # . prologue
+862     55/push-ebp
+863     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+864     # . save registers
+865     51/push-ecx
+866     # eax = _append-4(out, outend, &s->data[0], &s->data[s->size])
+867     # . . push &s->data[s->size]
+868     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         0/r32/eax   0x10/disp8      .                 # copy *(ebp+16) to eax
+869     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
+870     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
+871     51/push-ecx
+872     # . . push &s->data[0]
+873     8d/copy-address                 1/mod/*+disp8   0/rm32/eax    .           .             .           1/r32/ecx   4/disp8         .                 # copy eax+4 to ecx
+874     51/push-ecx
+875     # . . push outend
+876     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+877     # . . push out
+878     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+879     # . . call
+880     e8/call  _append-4/disp32
+881     # . . discard args
+882     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
+883 $_append-3:end:
+884     # . restore registers
+885     59/pop-to-ecx
+886     # . epilogue
+887     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+888     5d/pop-to-ebp
+889     c3/return
+890 
+891 # 4-argument variant of _append
+892 _append-4:  # out: (addr byte), outend: (addr byte), in: (addr byte), inend: (addr byte) -> num_bytes_appended/eax: int
+893     # . prologue
+894     55/push-ebp
+895     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+896     # . save registers
+897     51/push-ecx
+898     52/push-edx
+899     53/push-ebx
+900     56/push-esi
+901     57/push-edi
+902     # num_bytes_appended = 0
+903     b8/copy-to-eax  0/imm32
+904     # edi = out
+905     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
+906     # edx = outend
+907     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0xc/disp8       .                 # copy *(ebp+12) to edx
+908     # esi = in
+909     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0x10/disp8      .                 # copy *(ebp+16) to esi
+910     # ecx = inend
+911     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x14/disp8      .                 # copy *(ebp+20) to ecx
+912 $_append-4:loop:
+913     # if (in >= inend) break
+914     39/compare                      3/mod/direct    6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare esi with ecx
+915     73/jump-if-addr>=  $_append-4:end/disp8
+916     # if (out >= outend) abort  # just to catch test failures fast
+917     39/compare                      3/mod/direct    7/rm32/edi    .           .             .           2/r32/edx   .               .                 # compare edi with edx
+918     73/jump-if-addr>=  $_append-4:abort/disp8
+919     # *out = *in
+920     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           3/r32/BL    .               .                 # copy byte at *esi to BL
+921     88/copy-byte                    0/mod/indirect  7/rm32/edi    .           .             .           3/r32/BL    .               .                 # copy byte at BL to *edi
+922     # ++num_bytes_appended
+923     40/increment-eax
+924     # ++in
+925     46/increment-esi
+926     # ++out
+927     47/increment-edi
+928     eb/jump  $_append-4:loop/disp8
+929 $_append-4:end:
+930     # . restore registers
+931     5f/pop-to-edi
+932     5e/pop-to-esi
+933     5b/pop-to-ebx
+934     5a/pop-to-edx
+935     59/pop-to-ecx
+936     # . epilogue
+937     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+938     5d/pop-to-ebp
+939     c3/return
+940 
+941 $_append-4:abort:
+942     # . _write(2/stderr, error)
+943     # . . push args
+944     68/push  "stream overflow\n"/imm32
+945     68/push  2/imm32/stderr
+946     # . . call
+947     e8/call  _write/disp32
+948     # . . discard args
+949     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+950     # . syscall(exit, 1)
+951     bb/copy-to-ebx  1/imm32
+952     e8/call  syscall_exit/disp32
+953     # never gets here
+954 
+955 == data
+956 
+957 _test-stream-line-ABABA:  # (stream byte)
+958     # write
+959     8/imm32
+960     # read
+961     0/imm32
+962     # size
+963     8/imm32
+964     # data
+965     41 42 41 42 41 0a 00 00  # 8 bytes
+966 
+967 _test-stream-empty:  # (stream byte)
+968     # write
+969     0/imm32
+970     # read
+971     0/imm32
+972     # size
+973     8/imm32
+974     # data
+975     00 00 00 00 00 00 00 00  # 8 bytes
+976 
+977 _test-stream-filled:  # (stream byte)
+978     # write
+979     8/imm32
+980     # read
+981     0/imm32
+982     # size
+983     8/imm32
+984     # data
+985     41 41 41 41 0a 41 41 41  # 8 bytes
+986 
+987 # . . vim:nowrap:textwidth=0
+
+ + + -- cgit 1.4.1-2-gfad0