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