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 Stdout:
 11     # file descriptor or (address stream)
 12     01 00 00 00  # 1 = standard output
 13     # current write index
 14     00 00 00 00
 15     # current read index
 16     00 00 00 00
 17     # length (8)
 18     08 00 00 00
 19     # data
 20     00 00 00 00 00 00 00 00  # 8 bytes
 21 
 22 # TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But
 23 # I don't want to type in 1024 bytes here.
 24 
 25 == code
 26 #   instruction                     effective address                                                   register    displacement    immediate
 27 # . op          subop               mod             rm32          base        index         scale       r32
 28 # . 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
 29 
 30 # main:
 31     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
 32     # syscall(exit, Num-test-failures)
 33     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 34     b8/copy-to-EAX  1/imm32/exit
 35     cd/syscall  0x80/imm8
 36 
 37 # Write lower byte of 'n' to 'f'.
 38 write-byte:  # f : (address buffered-file), n : int -> <void>
 39     # . prolog
 40     55/push-EBP
 41     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 42     # . save registers
 43     51/push-ECX
 44     57/push-EDI
 45     # EDI = f
 46     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
 47     # ECX = f->write
 48     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
 49     # if (f->write >= f->length) flush and clear f's stream
 50     3b/compare                      1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   0xc/disp8       .                 # compare ECX with *(EDI+12)
 51     7c/jump-if-lesser  $write-byte:to-stream/disp8
 52     # . flush(f)
 53     # . . push args
 54     57/push-EDI
 55     # . . call
 56     e8/call  flush/disp32
 57     # . . discard args
 58     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 59     # . clear-stream(stream = f+4)
 60     # . . push args
 61     8d/copy-address                 1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EDI+4 to EAX
 62     50/push-EAX
 63     # . . call
 64     e8/call  clear-stream/disp32
 65     # . . discard args
 66     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 67     # . f->write must now be 0; update its cache at ECX
 68     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
 69 $write-byte:to-stream:
 70     # write to stream
 71     # f->data[f->write] = LSB(n)
 72     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 73     8a/copy-byte                    1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/AL    0xc/disp8       .                 # copy byte at *(EBP+12) to AL
 74     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)
 75     # ++f->write
 76     ff          0/subop/increment   1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         .                 # increment *(EDI+4)
 77 $write-byte:end:
 78     # . restore registers
 79     5f/pop-to-EDI
 80     59/pop-to-ECX
 81     # . epilog
 82     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 83     5d/pop-to-EBP
 84     c3/return
 85 
 86 flush:  # f : (address buffered-file) -> <void>
 87     # . prolog
 88     55/push-EBP
 89     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 90     # . save registers
 91     50/push-EAX
 92     51/push-ECX
 93     # EAX = f
 94     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
 95     # write-stream(f->fd, data = f+4)
 96       # . . push args
 97     8d/copy-address                 1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy EAX+4 to ECX
 98     51/push-ECX
 99     ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
100       # . . call
101     e8/call  write-stream/disp32
102       # . . discard args
103     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
104 $flush:end:
105     # . restore registers
106     59/pop-to-ECX
107     58/pop-to-EAX
108     # . epilog
109     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
110     5d/pop-to-EBP
111     c3/return
112 
113 # - tests
114 
115 test-write-byte-single:
116     # - check that write-byte writes to first byte of 'file'
117     # setup
118     # . clear-stream(_test-stream)
119     # . . push args
120     68/push  _test-stream/imm32
121     # . . call
122     e8/call  clear-stream/disp32
123     # . . discard args
124     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
125     # . clear-stream(_test-buffered-file+4)
126     # . . push args
127     b8/copy-to-EAX  _test-buffered-file/imm32
128     05/add-to-EAX  4/imm32
129     50/push-EAX
130     # . . call
131     e8/call  clear-stream/disp32
132     # . . discard args
133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
134     # write-byte(_test-buffered-file, 'A')
135     # . . push args
136     68/push  0x41/imm32
137     68/push  _test-buffered-file/imm32
138     # . . call
139     e8/call  write-byte/disp32
140     # . . discard args
141     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
142     # flush(_test-buffered-file)
143     # . . push args
144     68/push  _test-buffered-file/imm32
145     # . . call
146     e8/call  flush/disp32
147     # . . discard args
148     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
149     # check-ints-equal(*_test-stream->data, 'A', msg)
150     # . . push args
151     68/push  "F - test-write-byte-single"/imm32
152     68/push  0x41/imm32
153     # . . push *_test-stream->data
154     b8/copy-to-EAX  _test-stream/imm32
155     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
156     # . . call
157     e8/call  check-ints-equal/disp32
158     # . . discard args
159     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
160     # . end
161     c3/return
162 
163 test-write-byte-multiple-flushes:
164     # - check that write-byte correctly flushes buffered data
165     # setup
166     # . clear-stream(_test-stream)
167     # . . push args
168     68/push  _test-stream/imm32
169     # . . call
170     e8/call  clear-stream/disp32
171     # . . discard args
172     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
173     # . clear-stream(_test-buffered-file+4)
174     # . . push args
175     b8/copy-to-EAX  _test-buffered-file/imm32
176     05/add-to-EAX  4/imm32
177     50/push-EAX
178     # . . call
179     e8/call  clear-stream/disp32
180     # . . discard args
181     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
182     # fill up the buffer for _test-buffered-file
183     # . write(_test-buffered-file+4, 'abcdef')
184     # . . push args
185     68/push  "abcdef"/imm32
186     b8/copy-to-EAX  _test-buffered-file/imm32
187     05/add-to-EAX  4/imm32
188     50/push-EAX
189     # . . call
190     e8/call  write/disp32
191     # . . discard args
192     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
193     # write-byte(_test-buffered-file, 'g')
194     # . . push args
195     68/push  0x67/imm32
196     68/push  _test-buffered-file/imm32
197     # . . call
198     e8/call  write-byte/disp32
199     # . . discard args
200     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
201     # flush(_test-buffered-file)
202     # . . push args
203     68/push  _test-buffered-file/imm32
204     # . . call
205     e8/call  flush/disp32
206     # . . discard args
207     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
208     # check-ints-equal(_test-stream->data[0..3], 'abcd', msg)
209     # . . push args
210     68/push  "F - test-write-byte-multiple-flushes: 1"/imm32
211     68/push  0x64636261/imm32
212     # . . push *_test-stream->data
213     b8/copy-to-EAX  _test-stream/imm32
214     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
215     # . . call
216     e8/call  check-ints-equal/disp32
217     # . . discard args
218     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
219     # check-ints-equal(_test-stream->data[4..8], 'efg', msg)
220     # . . push args
221     68/push  "F - test-write-byte-multiple-flushes"/imm32
222     68/push  0x00676665/imm32
223     # . . push *_test-stream->data
224     b8/copy-to-EAX  _test-stream/imm32
225     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0x10/disp8      .                 # push *(EAX+16)
226     # . . call
227     e8/call  check-ints-equal/disp32
228     # . . discard args
229     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
230     # . end
231     c3/return
232 
233 # . . vim:nowrap:textwidth=0