https://github.com/akkartik/mu/blob/main/115write-byte.subx
 1 # Write a single byte to a stream.
 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 lower byte of 'n' to 'f'.
12 append-byte:  # f: (addr stream byte), n: int
13     # . prologue
14     55/push-ebp
15     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
16     # . save registers
17     50/push-eax
18     51/push-ecx
19     57/push-edi
20     # edi = f
21     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
22     # ecx = f->write
23     8b/copy                         0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # copy *edi to ecx
24     # if (f->write >= f->size) abort
25     3b/compare                      1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   8/disp8         .                 # compare ecx with *(edi+8)
26     7d/jump-if->=  $append-byte:abort/disp8
27 $append-byte:to-stream:
28     # write to stream
29     # f->data[f->write] = LSB(n)
30     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
31     8a/copy-byte                    1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ebp+12) to AL
32     88/copy-byte                    1/mod/*+disp8   4/rm32/sib    7/base/edi  1/index/ecx   .           0/r32/AL    0xc/disp8       .                 # copy AL to *(edi+ecx+12)
33     # ++f->write
34     ff          0/subop/increment   0/mod/indirect  7/rm32/edi    .           .             .           .           .               .                 # increment *edi
35 $append-byte:end:
36     # . restore registers
37     5f/pop-to-edi
38     59/pop-to-ecx
39     58/pop-to-eax
40     # . epilogue
41     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
42     5d/pop-to-ebp
43     c3/return
44 
45 $append-byte:abort:
46     (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "append-byte: out of space\n" 3 0)  # 3=cyan
47     {
48       eb/jump loop/disp8
49     }
50     # never gets here
51 
52 test-append-byte-single:
53     # - check that append-byte writes to first byte of 'file'
54     # setup
55     # . clear-stream(_test-stream)
56     # . . push args
57     68/push  _test-stream/imm32
58     # . . call
59     e8/call  clear-stream/disp32
60     # . . discard args
61     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
62     # append-byte(_test-stream, 'A')
63     # . . push args
64     68/push  0x41/imm32
65     68/push  _test-stream/imm32
66     # . . call
67     e8/call  append-byte/disp32
68     # . . discard args
69     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
70     # check-stream-equal(_test-stream, "A", msg)
71     # . . push args
72     68/push  "F - test-append-byte-single"/imm32
73     68/push  "A"/imm32
74     68/push  _test-stream/imm32
75     # . . call
76     e8/call  check-stream-equal/disp32
77     # . . discard args
78     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
79     # . end
80     c3/return
81 
82 # . . vim:nowrap:textwidth=0