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