https://github.com/akkartik/mu/blob/master/115write-byte.subx
  1 # write-byte-buffered: add a single byte to a buffered-file.
  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:  # buffered-file
 11     # file descriptor or (addr stream byte)
 12     1/imm32  # standard output
 13 $Stdout->buffer:
 14     # inlined fields for a stream
 15     #   current write index
 16     0/imm32
 17     #   current read index
 18     0/imm32
 19     #   size
 20     8/imm32
 21     #   data
 22     00 00 00 00 00 00 00 00  # 8 bytes
 23 
 24 # TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But
 25 # I don't want to type in 1024 bytes here.
 26 
 27 == code
 28 #   instruction                     effective address                                                   register    displacement    immediate
 29 # . op          subop               mod             rm32          base        index         scale       r32
 30 # . 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
 31 
 32 # Write lower byte of 'n' to 'f'.
 33 write-byte-buffered:  # f: (addr buffered-file), n: int
 34     # . prologue
 35     55/push-ebp
 36     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 37     # . save registers
 38     51/push-ecx
 39     57/push-edi
 40     # edi = f
 41     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
 42     # ecx = f->write
 43     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(edi+4) to ecx
 44     # if (f->write >= f->size) flush and clear f's stream
 45     3b/compare                      1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   0xc/disp8       .                 # compare ecx with *(edi+12)
 46     7c/jump-if-<  $write-byte-buffered:to-stream/disp8
 47     # . flush(f)
 48     # . . push args
 49     57/push-edi
 50     # . . call
 51     e8/call  flush/disp32
 52     # . . discard args
 53     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 54     # . clear-stream(stream = f+4)
 55     # . . push args
 56     8d/copy-address                 1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy edi+4 to eax
 57     50/push-eax
 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     # . f->write must now be 0; update its cache at ecx
 63     31/xor                          3/mod/direct    1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # clear ecx
 64 $write-byte-buffered:to-stream:
 65     # write to stream
 66     # f->data[f->write] = LSB(n)
 67     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
 68     8a/copy-byte                    1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ebp+12) to AL
 69     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)
 70     # ++f->write
 71     ff          0/subop/increment   1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         .                 # increment *(edi+4)
 72 $write-byte-buffered:end:
 73     # . restore registers
 74     5f/pop-to-edi
 75     59/pop-to-ecx
 76     # . epilogue
 77     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 78     5d/pop-to-ebp
 79     c3/return
 80 
 81 flush:  # f: (addr buffered-file)
 82     # . prologue
 83     55/push-ebp
 84     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 85     # . save registers
 86     50/push-eax
 87     51/push-ecx
 88     # eax = f
 89     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
 90     # write-stream(f->fd, data = f+4)
 91       # . . push args
 92     8d/copy-address                 1/mod/*+disp8   0/rm32/eax    .           .             .           1/r32/ecx   4/disp8         .                 # copy eax+4 to ecx
 93     51/push-ecx
 94     ff          6/subop/push        0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # push *eax
 95       # . . call
 96     e8/call  write-stream/disp32
 97       # . . discard args
 98     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 99 $flush:end:
100     # . restore registers
101     59/pop-to-ecx
102     58/pop-to-eax
103     # . epilogue
104     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
105     5d/pop-to-ebp
106     c3/return
107 
108 test-write-byte-buffered-single:
109     # - check that write-byte-buffered writes to first byte of 'file'
110     # setup
111     # . clear-stream(_test-stream)
112     # . . push args
113     68/push  _test-stream/imm32
114     # . . call
115     e8/call  clear-stream/disp32
116     # . . discard args
117     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
118     # . clear-stream($_test-buffered-file->buffer)
119     # . . push args
120     68/push  $_test-buffered-file->buffer/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     # write-byte-buffered(_test-buffered-file, 'A')
126     # . . push args
127     68/push  0x41/imm32
128     68/push  _test-buffered-file/imm32
129     # . . call
130     e8/call  write-byte-buffered/disp32
131     # . . discard args
132     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
133     # flush(_test-buffered-file)
134     # . . push args
135     68/push  _test-buffered-file/imm32
136     # . . call
137     e8/call  flush/disp32
138     # . . discard args
139     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
140     # check-stream-equal(_test-stream, "A", msg)
141     # . . push args
142     68/push  "F - test-write-byte-buffered-single"/imm32
143     68/push  "A"/imm32
144     68/push  _test-stream/imm32
145     # . . call
146     e8/call  check-stream-equal/disp32
147     # . . discard args
148     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
149     # . end
150     c3/return
151 
152 test-write-byte-buffered-multiple-flushes:
153     # - check that write-byte-buffered correctly flushes buffered data
154     # setup
155     # . clear-stream(_test-stream)
156     # . . push args
157     68/push  _test-stream/imm32
158     # . . call
159     e8/call  clear-stream/disp32
160     # . . discard args
161     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
162     # . clear-stream($_test-buffered-file->buffer)
163     # . . push args
164     68/push  $_test-buffered-file->buffer/imm32
165     # . . call
166     e8/call  clear-stream/disp32
167     # . . discard args
168     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
169     # fill up the buffer for _test-buffered-file
170     # . write($_test-buffered-file->buffer, "abcdef")
171     # . . push args
172     68/push  "abcdef"/imm32
173     68/push  $_test-buffered-file->buffer/imm32
174     # . . call
175     e8/call  write/disp32
176     # . . discard args
177     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
178     # write-byte-buffered(_test-buffered-file, 'g')
179     # . . push args
180     68/push  0x67/imm32
181     68/push  _test-buffered-file/imm32
182     # . . call
183     e8/call  write-byte-buffered/disp32
184     # . . discard args
185     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
186     # flush(_test-buffered-file)
187     # . . push args
188     68/push  _test-buffered-file/imm32
189     # . . call
190     e8/call  flush/disp32
191     # . . discard args
192     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
193     # check-stream-equal(_test-stream, "abcdefg", msg)
194     # . . push args
195     68/push  "F - test-write-byte-buffered-multiple-flushes"/imm32
196     68/push  "abcdefg"/imm32
197     68/push  _test-stream/imm32
198     # . . call
199     e8/call  check-stream-equal/disp32
200     # . . discard args
201     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
202     # . end
203     c3/return
204 
205 # - variant without buffering
206 
207 # Write lower byte of 'n' to 'f'.
208 append-byte:  # f: (addr stream byte), n: int
209     # . prologue
210     55/push-ebp
211     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
212     # . save registers
213     51/push-ecx
214     57/push-edi
215     # edi = f
216     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
217     # ecx = f->write
218     8b/copy                         0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # copy *edi to ecx
219     # if (f->write >= f->size) abort
220     3b/compare                      1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   8/disp8         .                 # compare ecx with *(edi+8)
221     7d/jump-if->=  $append-byte:abort/disp8
222 $append-byte:to-stream:
223     # write to stream
224     # f->data[f->write] = LSB(n)
225     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
226     8a/copy-byte                    1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ebp+12) to AL
227     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)
228     # ++f->write
229     ff          0/subop/increment   0/mod/indirect  7/rm32/edi    .           .             .           .           .               .                 # increment *edi
230 $append-byte:end:
231     # . restore registers
232     5f/pop-to-edi
233     59/pop-to-ecx
234     # . epilogue
235     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
236     5d/pop-to-ebp
237     c3/return
238 
239 $append-byte:abort:
240     # . _write(2/stderr, error)
241     # . . push args
242     68/push  "append-byte: out of space\n"/imm32
243     68/push  2/imm32/stderr
244     # . . call
245     e8/call  _write/disp32
246     # . . discard args
247     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
248     # . syscall(exit, 1)
249     bb/copy-to-ebx  1/imm32
250     e8/call  syscall_exit/disp32
251     # never gets here
252 
253 test-append-byte-single:
254     # - check that append-byte writes to first byte of 'file'
255     # setup
256     # . clear-stream(_test-stream)
257     # . . push args
258     68/push  _test-stream/imm32
259     # . . call
260     e8/call  clear-stream/disp32
261     # . . discard args
262     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
263     # append-byte(_test-stream, 'A')
264     # . . push args
265     68/push  0x41/imm32
266     68/push  _test-stream/imm32
267     # . . call
268     e8/call  append-byte/disp32
269     # . . discard args
270     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
271     # check-stream-equal(_test-stream, "A", msg)
272     # . . push args
273     68/push  "F - test-append-byte-single"/imm32
274     68/push  "A"/imm32
275     68/push  _test-stream/imm32
276     # . . call
277     e8/call  check-stream-equal/disp32
278     # . . discard args
279     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
280     # . end
281     c3/return
282 
283 == data
284 
285 _test-output-stream:  # (stream byte)
286     # current write index
287     0/imm32
288     # current read index
289     0/imm32
290     # size
291     0x400/imm32  # 1024 bytes
292     # data (64 lines x 16 bytes/line)
293     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
294     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
295     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
296     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
297     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
298     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
299     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
300     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
301     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
302     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
303     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
304     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
305     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
306     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
307     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
308     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
309     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
310     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
311     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
312     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
313     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
314     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
315     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
316     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
317     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
318     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
319     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
320     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
321     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
322     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
323     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
324     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
325     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
326     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
327     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
328     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
329     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
330     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
331     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
332     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
333     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
334     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
335     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
336     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
337     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
338     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
339     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
340     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
341     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
342     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
343     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
344     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
345     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
346     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
347     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
348     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
349     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
350     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
351     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
352     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
353     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
354     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
355     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
356     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
357 
358 # a test buffered file for _test-output-stream
359 _test-output-buffered-file:  # buffered-file
360     # file descriptor or (addr stream byte)
361     _test-output-stream/imm32
362 $_test-output-buffered-file->buffer:
363     # current write index
364     0/imm32
365     # current read index
366     0/imm32
367     # size
368     6/imm32
369     # data
370     00 00 00 00 00 00  # 6 bytes
371 
372 _test-error-stream:  # (stream byte)
373     # current write index
374     0/imm32
375     # current read index
376     0/imm32
377     # line
378     0x80/imm32  # 128 bytes
379     # data (8 lines x 16 bytes/line)
380     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
381     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
382     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
383     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
384     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
385     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
386     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
387     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
388 
389 # a test buffered file for _test-error-stream
390 _test-error-buffered-file:  # buffered-file
391     # file descriptor or (addr stream byte)
392     _test-error-stream/imm32
393 $_test-error-buffered-file->buffer:
394     # current write index
395     0/imm32
396     # current read index
397     0/imm32
398     # size
399     6/imm32
400     # data
401     00 00 00 00 00 00  # 6 bytes
402 
403 # . . vim:nowrap:textwidth=0