https://github.com/akkartik/mu/blob/main/125write-stream-data.subx
  1 == code
  2 #   instruction                     effective address                                                   register    displacement    immediate
  3 # . op          subop               mod             rm32          base        index         scale       r32
  4 # . 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
  5 
  6 # write an entire stream's contents to a buffered-file
  7 # ways to do this:
  8 #   - construct a 'maximal slice' and pass it to write-slice-buffered
  9 #   - flush the buffered-file and pass the stream directly to its fd (disabling buffering)
 10 # we'll go with the first way for now
 11 write-stream-data:  # f: (addr buffered-file), s: (addr stream byte)
 12     # . prologue
 13     55/push-ebp
 14     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 15     # . save registers
 16     50/push-eax
 17     51/push-ecx
 18     56/push-esi
 19     # esi = s
 20     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
 21     # var slice/ecx: slice = {s->data, &s->data[s->write]}
 22     # . push &s->data[s->write]
 23     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
 24     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  0/index/eax   .           0/r32/eax   0xc/disp8       .                 # copy esi+eax+12 to eax
 25     50/push-eax
 26     # . push s->data
 27     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   0xc/disp8       .                 # copy esi+12 to eax
 28     50/push-eax
 29     # . ecx = esp
 30     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 31     # write-slice-buffered(f, slice)
 32     # . . push args
 33     51/push-ecx
 34     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 35     # . . call
 36     e8/call  write-slice-buffered/disp32
 37     # . . discard args
 38     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 39 $write-stream-data:end:
 40     # . restore locals
 41     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 42     # . restore registers
 43     5e/pop-to-esi
 44     59/pop-to-ecx
 45     58/pop-to-eax
 46     # . epilogue
 47     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 48     5d/pop-to-ebp
 49     c3/return
 50 
 51 test-write-stream-data:
 52     # . prologue
 53     55/push-ebp
 54     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 55     # setup
 56     # . clear-stream(_test-output-stream)
 57     # . . push args
 58     68/push  _test-output-stream/imm32
 59     # . . call
 60     e8/call  clear-stream/disp32
 61     # . . discard args
 62     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 63     # . clear-stream($_test-output-buffered-file->buffer)
 64     # . . push args
 65     68/push  $_test-output-buffered-file->buffer/imm32
 66     # . . call
 67     e8/call  clear-stream/disp32
 68     # . . discard args
 69     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 70     # . clear-stream(_test-input-stream)
 71     # . . push args
 72     68/push  _test-input-stream/imm32
 73     # . . call
 74     e8/call  clear-stream/disp32
 75     # . . discard args
 76     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 77     # initialize input
 78     # . write(_test-input-stream, "abcd")
 79     # . . push args
 80     68/push  "abcd"/imm32
 81     68/push  _test-input-stream/imm32
 82     # . . call
 83     e8/call  write/disp32
 84     # . . discard args
 85     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 86     # write-stream-data(_test-output-buffered-file, _test-input-stream)
 87     # . . push args
 88     68/push  _test-input-stream/imm32
 89     68/push  _test-output-buffered-file/imm32
 90     # . . call
 91     e8/call  write-stream-data/disp32
 92     # . . discard args
 93     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 94     # check that the write happened as expected
 95     # . flush(_test-output-buffered-file)
 96     # . . push args
 97     68/push  _test-output-buffered-file/imm32
 98     # . . call
 99     e8/call  flush/disp32
100     # . . discard args
101     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
102     # . check-stream-equal(_test-output-stream, "abcd", msg)
103     # . . push args
104     68/push  "F - test-write-stream-data"/imm32
105     68/push  "abcd"/imm32
106     68/push  _test-output-stream/imm32
107     # . . call
108     e8/call  check-stream-equal/disp32
109     # . . discard args
110     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
111     # . epilogue
112     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
113     5d/pop-to-ebp
114     c3/return