https://github.com/akkartik/mu/blob/main/108write.subx

      1 # write: write to in-memory streams
      2 #
      3 # We need to do this in machine code because streams need to be opaque types,
      4 # and we don't yet support opaque types in Mu.
      5
      6 == code
      7 #   instruction                     effective address                                                   register    displacement    immediate
      8 # . op          subop               mod             rm32          base        index         scale       r32
      9 # . 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
     10
     11 write:  # f: (addr stream byte), s: (addr array byte)
     12     # . prologue
     13     55/push-ebp
     14     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     15     # if (s == 0) return
     16     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       0/imm32           # compare *(ebp+12)
     17     74/jump-if-=  $write:end/disp8
     18     # . save registers
     19     50/push-eax
     20     51/push-ecx
     21     52/push-edx
     22     53/push-ebx
     23     # ecx = f
     24     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
     25     # edx = f->write
     26     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
     27     # ebx = f->size
     28     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           3/r32/ebx   8/disp8         .                 # copy *(ecx+8) to ebx
     29     # eax = _append-3(&f->data[f->write], &f->data[f->size], s)
     30     # . . push s
     31     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     32     # . . push &f->data[f->size]
     33     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ecx  3/index/ebx   .           3/r32/ebx   0xc/disp8       .                 # copy ecx+ebx+12 to ebx
     34     53/push-ebx
     35     # . . push &f->data[f->write]
     36     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ecx  2/index/edx   .           3/r32/ebx   0xc/disp8       .                 # copy ecx+edx+12 to ebx
     37     53/push-ebx
     38     # . . call
     39     e8/call  _append-3/disp32
     40     # . . discard args
     41     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     42     # f->write += eax
     43     01/add                          0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # add eax to *ecx
     44     # . restore registers
     45     5b/pop-to-ebx
     46     5a/pop-to-edx
     47     59/pop-to-ecx
     48     58/pop-to-eax
     49 $write:end:
     50     # . epilogue
     51     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     52     5d/pop-to-ebp
     53     c3/return
     54
     55 test-write-single:
     56     # clear-stream(_test-stream)
     57     # . . push args
     58     68/push  _test-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     # write(_test-stream, "Ab")
     64     # . . push args
     65     68/push  "Ab"/imm32
     66     68/push  _test-stream/imm32
     67     # . . call
     68     e8/call  write/disp32
     69     # . . discard args
     70     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     71     # check-ints-equal(*_test-stream->data, 41/A 62/b 00 00, msg)
     72     # . . push args
     73     68/push  "F - test-write-single"/imm32
     74     68/push  0x006241/imm32/Ab
     75     # . . push *_test-stream->data
     76     b8/copy-to-eax  _test-stream/imm32
     77     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           0xc/disp8       .                 # push *(eax+12)
     78     # . . call
     79     e8/call  check-ints-equal/disp32
     80     # . . discard args
     81     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     82     # end
     83     c3/return
     84
     85 test-write-appends:
     86     # clear-stream(_test-stream)
     87     # . . push args
     88     68/push  _test-stream/imm32
     89     # . . call
     90     e8/call  clear-stream/disp32
     91     # . . discard args
     92     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     93     # write(_test-stream, "C")
     94     # . . push args
     95     68/push  "C"/imm32
     96     68/push  _test-stream/imm32
     97     # . . call
     98     e8/call  write/disp32
     99     # . . discard args
    100     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    101     # write(_test-stream, "D")
    102     # . . push args
    103     68/push  "D"/imm32
    104     68/push  _test-stream/imm32
    105     # . . call
    106     e8/call  write/disp32
    107     # . . discard args
    108     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    109     # check-ints-equal(*_test-stream->data, 43/C 44/D 00 00, msg)
    110     # . . push args
    111     68/push  "F - test-write-appends"/imm32
    112     68/push  0x00004443/imm32/C-D
    113     # . . push *_test-stream->data
    114     b8/copy-to-eax  _test-stream/imm32
    115     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           0xc/disp8       .                 # push *(eax+12)
    116     # . . call
    117     e8/call  check-ints-equal/disp32
    118     # . . discard args
    119     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    120     # end
    121     c3/return
    122
    123 == data
    124
    125 _test-stream:  # (stream byte)
    126     # current write index
    127     0/imm32
    128     # current read index
    129     0/imm32
    130     # size
    131     0x10/imm32
    132     # data (2 lines x 8 bytes/line)
    133     00 00 00 00 00 00 00 00
    134     00 00 00 00 00 00 00 00
    135
    136 == code
    137
    138 # 3-argument variant of _append
    139 _append-3:  # out: (addr byte), outend: (addr byte), s: (addr array byte) -> num_bytes_appended/eax
    140     # . prologue
    141     55/push-ebp
    142     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    143     # . save registers
    144     51/push-ecx
    145     # eax = _append-4(out, outend, &s->data[0], &s->data[s->size])
    146     # . . push &s->data[s->size]
    147     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         0/r32/eax   0x10/disp8      .                 # copy *(ebp+16) to eax
    148     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    149     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
    150     51/push-ecx
    151     # . . push &s->data[0]
    152     8d/copy-address                 1/mod/*+disp8   0/rm32/eax    .           .             .           1/r32/ecx   4/disp8         .                 # copy eax+4 to ecx
    153     51/push-ecx
    154     # . . push outend
    155     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    156     # . . push out
    157     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    158     # . . call
    159     e8/call  _append-4/disp32
    160     # . . discard args
    161     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    162 $_append-3:end:
    163     # . restore registers
    164     59/pop-to-ecx
    165     # . epilogue
    166     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    167     5d/pop-to-ebp
    168     c3/return
    169
    170 # 4-argument variant of _append
    171 _append-4:  # out: (addr byte), outend: (addr byte), in: (addr byte), inend: (addr byte) -> num_bytes_appended/eax: int
    172     # . prologue
    173     55/push-ebp
    174     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    175     # . save registers
    176     51/push-ecx
    177     52/push-edx
    178     53/push-ebx
    179     56/push-esi
    180     57/push-edi
    181     # num_bytes_appended = 0
    182     b8/copy-to-eax  0/imm32
    183     # edi = out
    184     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
    185     # edx = outend
    186     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0xc/disp8       .                 # copy *(ebp+12) to edx
    187     # esi = in
    188     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0x10/disp8      .                 # copy *(ebp+16) to esi
    189     # ecx = inend
    190     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x14/disp8      .                 # copy *(ebp+20) to ecx
    191 $_append-4:loop:
    192     # if (in >= inend) break
    193     39/compare                      3/mod/direct    6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare esi with ecx
    194     73/jump-if-addr>=  $_append-4:end/disp8
    195     # if (out >= outend) abort  # just to catch test failures fast
    196     39/compare                      3/mod/direct    7/rm32/edi    .           .             .           2/r32/edx   .               .                 # compare edi with edx
    197     73/jump-if-addr>=  $_append-4:end/disp8  # TODO: abort
    198     # *out = *in
    199     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           3/r32/BL    .               .                 # copy byte at *esi to BL
    200     88/copy-byte                    0/mod/indirect  7/rm32/edi    .           .             .           3/r32/BL    .               .                 # copy byte at BL to *edi
    201     # ++num_bytes_appended
    202     40/increment-eax
    203     # ++in
    204     46/increment-esi
    205     # ++out
    206     47/increment-edi
    207     eb/jump  $_append-4:loop/disp8
    208 $_append-4:end:
    209     # . restore registers
    210     5f/pop-to-edi
    211     5e/pop-to-esi
    212     5b/pop-to-ebx
    213     5a/pop-to-edx
    214     59/pop-to-ecx
    215     # . epilogue
    216     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    217     5d/pop-to-ebp
    218     c3/return
    219
    220 # . . vim:nowrap:textwidth=0