https://github.com/akkartik/mu/blob/main/106stream.subx

     1 # streams: data structure for operating on arrays in a stateful manner
     2 #
     3 # A stream looks like this:
     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 #
     8 # some primitives for operating on streams:
     9 #   - clear-stream (clears everything but the data size)
    10 #   - rewind-stream (resets read pointer)
    11 #
    12 # We need to do this in machine code because streams need to be opaque types,
    13 # and we don't yet support opaque types in Mu.
    14
    15 == code
    16 #   instruction                     effective address                                                   register    displacement    immediate
    17 # . op          subop               mod             rm32          base        index         scale       r32
    18 # . 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
    19
    20 clear-stream:  # f: (addr stream byte)
    21     # . prologue
    22     55/push-ebp
    23     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    24     # . save registers
    25     50/push-eax
    26     51/push-ecx
    27     # eax = f
    28     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
    29     # var count/ecx: int = f->size
    30     8b/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(eax+8) to ecx
    31     # var max/ecx: (addr byte) = &f->data[f->size]
    32     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   0xc/disp8       .                 # copy eax+ecx+12 to ecx
    33     # f->write = 0
    34     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # copy to *eax
    35     # f->read = 0
    36     c7          0/subop/copy        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         0/imm32           # copy to *(eax+4)
    37     # - clear all stream data
    38     # - this isn't strictly necessary, and it can slow things down *a lot*, but better safe than sorry.
    39     # var curr/eax: (addr byte) = f->data
    40     81          0/subop/add         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xc/imm32         # add to eax
    41 $clear-stream:loop:
    42     # if (curr >= max) break
    43     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
    44     73/jump-if-addr>=  $clear-stream:end/disp8
    45     # *curr = 0
    46     c6          0/subop/copy-byte   0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm8            # copy byte to *eax
    47     # ++curr
    48     40/increment-eax
    49     eb/jump  $clear-stream:loop/disp8
    50 $clear-stream:end:
    51     # . restore registers
    52     59/pop-to-ecx
    53     58/pop-to-eax
    54     # . epilogue
    55     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    56     5d/pop-to-ebp
    57     c3/return
    58
    59 rewind-stream:  # f: (addr stream byte)
    60     # . prologue
    61     55/push-ebp
    62     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    63     # . save registers
    64     50/push-eax
    65     # eax = f
    66     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
    67     # f->read = 0
    68     c7          0/subop/copy        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         0/imm32           # copy to *(eax+4)
    69 $rewind-stream:end:
    70     # . restore registers
    71     58/pop-to-eax
    72     # . epilogue
    73     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    74     5d/pop-to-ebp
    75     c3/return
    76
    77 # . . vim:nowrap:textwidth=0