https://github.com/akkartik/mu/blob/master/subx/062write-byte.subx
  1 # write-byte: write a single byte to a buffered-file. The write may be buffered.
  2 # flush: write out any buffered writes to disk.
  3 #
  4 # TODO: Come up with a way to signal failure to write to disk. This is hard
  5 # since the failure may impact previous calls that were buffered.
  6 
  7 == data
  8 
  9 # The buffered file for standard output.
 10 
 11 Stdout:
 12     # file descriptor or (address stream)
 13     01 00 00 00  # 1 = standard output
 14     # current write index
 15     00 00 00 00
 16     # current read index
 17     00 00 00 00
 18     # length (8)
 19     08 00 00 00
 20     # data
 21     00 00 00 00 00 00 00 00  # 8 bytes
 22 
 23 # TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But
 24 # I don't want to type 1024 bytes here.
 25 
 26 == code
 27 #   instruction                     effective address                                                   register    displacement    immediate
 28 # . op          subop               mod             rm32          base        index         scale       r32
 29 # . 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
 30 
 31 # main:
 32     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
 33     # syscall(exit, Num-test-failures)
 34     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 35     b8/copy-to-EAX  1/imm32
 36     cd/syscall  0x80/imm8
 37 
 38 # Write lower byte of 'n' to 'f'.
 39 write-byte:  # f : (address buffered-file), n : num -> <void>
 40     # . prolog
 41     55/push-EBP
 42     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 43     # . save registers
 44     51/push-ECX
 45     57/push-EDI
 46     # EDI = f
 47     8b/copy                         1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
 48     # ECX = f->write
 49     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
 50     # if (f->write >= f->length) flush and clear f's stream
 51     3b/compare                      1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   0xc/disp8       .                 # compare ECX with *(EDI+12)
 52     7c/jump-if-lesser  $write-byte:to-stream/disp8
 53     # . flush(f)
 54     # . . push args
 55     57/push-EDI
 56     # . . call
 57     e8/call  flush/disp32
 58     # . . discard args
 59     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 60     # . clear-stream(stream = f+4)
 61     # . . push args
 62     8d/copy-address                 1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EDI+4 to EAX
 63     50/push-EAX
 64     # . . call
 65     e8/call  clear-stream/disp32
 66     # . . discard args
 67     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 68 $write-byte:to-stream:
 69     # write to stream
 70     # f->data[f->read] = LSB(n)
 71     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 72     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           0/r32/AL    0xc/disp8       .                 # copy byte at *(EBP+12) to AL
 73     88/copy-byte                    1/mod/*+disp8   4/rm32/sib    7/base/EDI  1/index/ECX   .           0/r32/AL    0x10/disp8      .                 # copy AL to *(EDI+ECX+16)
 74     # ++f->read
 75     ff          0/subop/increment   1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         .                 # increment *(EDI+4)
 76 $write-byte:end:
 77     # . restore registers
 78     5f/pop-to-EDI
 79     59/pop-to-ECX
 80     # . epilog
 81     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 82     5d/pop-to-EBP
 83     c3/return
 84 
 85 flush:  # f : (address buffered-file) -> <void>
 86     # . prolog
 87     55/push-EBP
 88     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 89     # . save registers
 90     50/push-EAX
 91     51/push-ECX
 92     # EAX = f
 93     8b/copy                         1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
 94     # write-stream(f->fd, data = f+4)
 95       # . . push args
 96     8d/copy-address                 1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy EAX+4 to ECX
 97     51/push-ECX
 98     ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
 99       # . . call
100     e8/call  write-stream/disp32
101       # . . discard args
102     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
103 $flush:end:
104     # . restore registers
105     59/pop-to-ECX
106     58/pop-to-EAX
107     # . epilog
108     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
109     5d/pop-to-EBP
110     c3/return
111 
112 # - tests
113 
114 test-write-byte-single:
115     # - check that read-byte returns first byte of 'file'
116     # setup
117     # . clear-stream(_test-stream)
118     # . . push args
119     68/push  _test-stream/imm32
120     # . . call
121     e8/call  clear-stream/disp32
122     # . . discard args
123     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
124     # . clear-stream(_test-buffered-file+4)
125     # . . push args
126     b8/copy-to-EAX  _test-buffered-file/imm32
127     05/add-to-EAX  4/imm32
128     50/push-EAX
129     # . . call
130     e8/call  clear-stream/disp32
131     # . . discard args
132     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
133     # write-byte(_test-buffered-file, 'A')
134     # . . push args
135     68/push  0x41/imm32
136     68/push  _test-buffered-file/imm32
137     # . . call
138     e8/call  write-byte/disp32
139     # . . discard args
140     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
141     # flush(_test-buffered-file)
142     # . . push args
143     68/push  _test-buffered-file/imm32
144     # . . call
145     e8/call  flush/disp32
146     # . . discard args
147     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
148     # check-ints-equal(*_test-stream->data, 'A', msg)
149     # . . push args
150     68/push  "F - test-write-byte-single"/imm32
151     68/push  0x41/imm32
152     # . . push *_test-stream->data
153     b8/copy-to-EAX  _test-stream/imm32
154     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
155     # . . call
156     e8/call  check-ints-equal/disp32
157     # . . discard args
158     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
159     # . end
160     c3/return
161 
162 # . . vim:nowrap:textwidth=0