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 try-write:  # f: (addr stream byte), s: (addr array byte) -> overflow?/eax: boolean
    139     # . prologue
    140     55/push-ebp
    141     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    142     # if (s == 0) return
    143     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       0/imm32           # compare *(ebp+12)
    144     74/jump-if-=  $try-write:end/disp8
    145     # . save registers
    146     51/push-ecx
    147     # if (f->size - f->write < s->size) return
    148     # . eax = f->size - f->write - s->size
    149     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
    150     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ecx+8) to eax
    151     2b/subtract                     0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract *ecx from eax
    152     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
    153     2b/subtract                     0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract *ecx from eax
    154     # . if (eax < 0) return
    155     3d/compare-eax-and  0/imm32
    156     7c/jump-if-<  $try-write:end/disp8
    157     # write(f, s)
    158     # . . push args
    159     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    160     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    161     # . . call
    162     e8/call  write/disp32
    163     # . . discard args
    164     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    165     # . restore registers
    166     59/pop-to-ecx
    167     # return 0
    168     b8/copy-to-eax  0/imm32
    169 $try-write:end:
    170     # . epilogue
    171     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    172     5d/pop-to-ebp
    173     c3/return
    174
    175 # probably a bad idea
    176 space-remaining-in-stream:  # f: (addr stream byte) -> n/eax: int
    177     # . prologue
    178     55/push-ebp
    179     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    180     # . save registers
    181     51/push-ecx
    182     # return f->size - f->write
    183     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
    184     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ecx+8) to eax
    185     2b/subtract                     0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract *ecx from eax
    186     # . restore registers
    187     59/pop-to-ecx
    188 $space-remaining-in-stream:end:
    189     # . epilogue
    190     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    191     5d/pop-to-ebp
    192     c3/return
    193
    194 stream-size:  # f: (addr stream byte) -> n/eax: int
    195     # . prologue
    196     55/push-ebp
    197     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    198     # . save registers
    199     51/push-ecx
    200     # return f->write
    201     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
    202     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
    203     # . restore registers
    204     59/pop-to-ecx
    205 $stream-size:end:
    206     # . epilogue
    207     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    208     5d/pop-to-ebp
    209     c3/return
    210
    211 # 3-argument variant of _append
    212 _append-3:  # out: (addr byte), outend: (addr byte), s: (addr array byte) -> num_bytes_appended/eax
    213     # . prologue
    214     55/push-ebp
    215     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    216     # . save registers
    217     51/push-ecx
    218     # if (outend - out < s->size) abort
    219     # . eax = f->size - f->write - s->size
    220     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
    221     2b/subtract                     1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # subtract *(ebp+8) from eax
    222     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x10/disp8      .                 # copy *(ebp+16) to ecx
    223     2b/subtract                     0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract *ecx from eax
    224     # . if (eax < 0) abort
    225     3d/compare-eax-and  0/imm32
    226     7c/jump-if-<  $_append-3:abort/disp8
    227     # eax = _append-4(out, outend, &s->data[0], &s->data[s->size])
    228     # . . push &s->data[s->size]
    229     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         0/r32/eax   0x10/disp8      .                 # copy *(ebp+16) to eax
    230     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    231     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
    232     51/push-ecx
    233     # . . push &s->data[0]
    234     8d/copy-address                 1/mod/*+disp8   0/rm32/eax    .           .             .           1/r32/ecx   4/disp8         .                 # copy eax+4 to ecx
    235     51/push-ecx
    236     # . . push outend
    237     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    238     # . . push out
    239     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    240     # . . call
    241     e8/call  _append-4/disp32
    242     # . . discard args
    243     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    244 $_append-3:end:
    245     # . restore registers
    246     59/pop-to-ecx
    247     # . epilogue
    248     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    249     5d/pop-to-ebp
    250     c3/return
    251
    252 $_append-3:abort:
    253     (abort "_append-3 about to overflow")  # 3=cyan
    254     # never gets here
    255
    256 # 4-argument variant of _append
    257 _append-4:  # out: (addr byte), outend: (addr byte), in: (addr byte), inend: (addr byte) -> num_bytes_appended/eax: int
    258     # . prologue
    259     55/push-ebp
    260     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    261     # . save registers
    262     51/push-ecx
    263     52/push-edx
    264     53/push-ebx
    265     56/push-esi
    266     57/push-edi
    267     # num_bytes_appended = 0
    268     b8/copy-to-eax  0/imm32
    269     # edi = out
    270     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
    271     # edx = outend
    272     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0xc/disp8       .                 # copy *(ebp+12) to edx
    273     # esi = in
    274     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0x10/disp8      .                 # copy *(ebp+16) to esi
    275     # ecx = inend
    276     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x14/disp8      .                 # copy *(ebp+20) to ecx
    277 $_append-4:loop:
    278     # if (in >= inend) break
    279     39/compare                      3/mod/direct    6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare esi with ecx
    280     73/jump-if-addr>=  $_append-4:end/disp8
    281     # if (out >= outend) abort  # just to catch test failures fast
    282     39/compare                      3/mod/direct    7/rm32/edi    .           .             .           2/r32/edx   .               .                 # compare edi with edx
    283     0f 83/jump-if-addr>=  $_append-4:abort/disp32
    284     # *out = *in
    285     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           3/r32/BL    .               .                 # copy byte at *esi to BL
    286     88/copy-byte                    0/mod/indirect  7/rm32/edi    .           .             .           3/r32/BL    .               .                 # copy byte at BL to *edi
    287     # ++num_bytes_appended
    288     40/increment-eax
    289     # ++in
    290     46/increment-esi
    291     # ++out
    292     47/increment-edi
    293     eb/jump  $_append-4:loop/disp8
    294 $_append-4:end:
    295     # . restore registers
    296     5f/pop-to-edi
    297     5e/pop-to-esi
    298     5b/pop-to-ebx
    299     5a/pop-to-edx
    300     59/pop-to-ecx
    301     # . epilogue
    302     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    303     5d/pop-to-ebp
    304     c3/return
    305
    306 $_append-4:abort:
    307     (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "_append-4: stream full at " 3 0)  # 3=cyan
    308     (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 %eax 3 0)
    309     (abort "")
    310     # never gets here
    311
    312 # . . vim:nowrap:textwidth=0