.
[mu.git] / linux / 112read-byte.subx
blobea6700b9b4c1d6935ff3d55e3c831e2cdaefa9ab
1 # read-byte-buffered: one higher-level abstraction atop 'read'.
3 # There are many situations where 'read' is a lot to manage, and we need
4 # to abstract some details away. One of them is when we want to read a file
5 # character by character. In this situation we follow C's FILE data structure,
6 # which manages the underlying file descriptor together with the buffer it
7 # reads into. We call our version 'buffered-file'. Should be useful with other
8 # primitives as well, in later layers.
10 == data
12 # The buffered file for standard input. Also illustrates the layout for
13 # buffered-file: a pointer to the backing store, followed by a 'buffer' stream
14 Stdin:  # buffered-file
15     # file descriptor or (addr stream byte)
16     0/imm32  # standard input
17 $Stdin->buffer:
18     # inlined fields for a stream
19     #   current write index
20     0/imm32
21     #   current read index
22     0/imm32
23     #   size
24     8/imm32
25     #   data
26     00 00 00 00 00 00 00 00  # 8 bytes
28 # TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But
29 # I don't want to type in 1024 bytes here.
31 == code
32 #   instruction                     effective address                                                   register    displacement    immediate
33 # . op          subop               mod             rm32          base        index         scale       r32
34 # . 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
36 # Return next byte value in eax, with top 3 bytes cleared.
37 # On reaching end of file, return 0xffffffff (Eof).
38 read-byte-buffered:  # f: (addr buffered-file) -> byte-or-Eof/eax: byte
39     # . prologue
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     56/push-esi
45     # esi = f
46     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
47     # ecx = f->read
48     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(esi+8) to ecx
49     # if (f->read >= f->write) populate stream from file
50     3b/compare                      1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # compare ecx with *(esi+4)
51     7c/jump-if-<  $read-byte-buffered:from-stream/disp8
52     # . clear-stream(stream = f+4)
53     # . . push args
54     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy esi+4 to eax
55     50/push-eax
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     # . f->read must now be 0; update its cache at ecx
61     31/xor                          3/mod/direct    1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # clear ecx
62     # . eax = read(f->fd, stream = f+4)
63     # . . push args
64     50/push-eax
65     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
66     # . . call
67     e8/call  read/disp32
68     # . . discard args
69     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
70     # if (eax == 0) return 0xffffffff
71     3d/compare-eax-and  0/imm32
72     75/jump-if-!=  $read-byte-buffered:from-stream/disp8
73     b8/copy-to-eax  0xffffffff/imm32/Eof
74     eb/jump  $read-byte-buffered:end/disp8
75 $read-byte-buffered:from-stream:
76     # byte-or-Eof = f->data[f->read]
77     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
78     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0x10/disp8      .                 # copy byte at *(esi+ecx+16) to AL
79     # ++f->read
80     ff          0/subop/increment   1/mod/*+disp8   6/rm32/esi    .           .             .           .           8/disp8         .                 # increment *(esi+8)
81 $read-byte-buffered:end:
82     # . restore registers
83     5e/pop-to-esi
84     59/pop-to-ecx
85     # . epilogue
86     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
87     5d/pop-to-ebp
88     c3/return
90 # - tests
92 test-read-byte-buffered-single:
93     # - check that read-byte-buffered returns first byte of 'file'
94     # setup
95     # . clear-stream(_test-stream)
96     # . . push args
97     68/push  _test-stream/imm32
98     # . . call
99     e8/call  clear-stream/disp32
100     # . . discard args
101     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
102     # . clear-stream(_test-buffered-file->buffer)
103     # . . push args
104     68/push  $_test-buffered-file->buffer/imm32
105     # . . call
106     e8/call  clear-stream/disp32
107     # . . discard args
108     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
109     # . write(_test-stream, "Ab")
110     # . . push args
111     68/push  "Ab"/imm32
112     68/push  _test-stream/imm32
113     # . . call
114     e8/call  write/disp32
115     # . . discard args
116     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
117     # read-byte-buffered(_test-buffered-file)
118     # . . push args
119     68/push  _test-buffered-file/imm32
120     # . . call
121     e8/call  read-byte-buffered/disp32
122     # . . discard args
123     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
124     # check-ints-equal(eax, 'A', msg)
125     # . . push args
126     68/push  "F - test-read-byte-buffered-single"/imm32
127     68/push  0x41/imm32
128     50/push-eax
129     # . . call
130     e8/call  check-ints-equal/disp32
131     # . . discard args
132     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
133     # . end
134     c3/return
136 test-read-byte-buffered-multiple:
137     # - call read-byte-buffered twice, check that second call returns second byte
138     # setup
139     # . clear-stream(_test-stream)
140     # . . push args
141     68/push  _test-stream/imm32
142     # . . call
143     e8/call  clear-stream/disp32
144     # . . discard args
145     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
146     # . clear-stream($_test-buffered-file->buffer)
147     # . . push args
148     68/push  $_test-buffered-file->buffer/imm32
149     # . . call
150     e8/call  clear-stream/disp32
151     # . . discard args
152     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
153     # . write(_test-stream, "Ab")
154     # . . push args
155     68/push  "Ab"/imm32
156     68/push  _test-stream/imm32
157     # . . call
158     e8/call  write/disp32
159     # . . discard args
160     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
161     # read-byte-buffered(_test-buffered-file)
162     # . . push args
163     68/push  _test-buffered-file/imm32
164     # . . call
165     e8/call  read-byte-buffered/disp32
166     # . . discard args
167     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
168     # read-byte-buffered(_test-buffered-file)
169     # . . push args
170     68/push  _test-buffered-file/imm32
171     # . . call
172     e8/call  read-byte-buffered/disp32
173     # . . discard args
174     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
175     # check-ints-equal(eax, 'b', msg)
176     # . . push args
177     68/push  "F - test-read-byte-buffered-multiple"/imm32
178     68/push  0x62/imm32
179     50/push-eax
180     # . . call
181     e8/call  check-ints-equal/disp32
182     # . . discard args
183     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
184     # . end
185     c3/return
187 test-read-byte-buffered-end-of-file:
188     # - call read-byte-buffered on an empty 'file', check that it returns Eof
189     # setup
190     # . clear-stream(_test-stream)
191     # . . push args
192     68/push  _test-stream/imm32
193     # . . call
194     e8/call  clear-stream/disp32
195     # . . discard args
196     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
197     # . clear-stream($_test-buffered-file->buffer)
198     # . . push args
199     68/push  $_test-buffered-file->buffer/imm32
200     # . . call
201     e8/call  clear-stream/disp32
202     # . . discard args
203     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
204     # read-byte-buffered(_test-buffered-file)
205     # . . push args
206     68/push  _test-buffered-file/imm32
207     # . . call
208     e8/call  read-byte-buffered/disp32
209     # . . discard args
210     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
211     # check-ints-equal(eax, 0xffffffff, msg)
212     # . . push args
213     68/push  "F - test-read-byte-buffered-end-of-file"/imm32
214     68/push  0xffffffff/imm32/Eof
215     50/push-eax
216     # . . call
217     e8/call  check-ints-equal/disp32
218     # . . discard args
219     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
220     # . end
221     c3/return
223 test-read-byte-buffered-refills-buffer:
224     # - consume buffered-file's buffer, check that next read-byte-buffered still works
225     # setup
226     # . clear-stream(_test-stream)
227     # . . push args
228     68/push  _test-stream/imm32
229     # . . call
230     e8/call  clear-stream/disp32
231     # . . discard args
232     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
233     # . clear-stream($_test-buffered-file->buffer)
234     # . . push args
235     68/push  $_test-buffered-file->buffer/imm32
236     # . . call
237     e8/call  clear-stream/disp32
238     # . . discard args
239     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
240     # . write(_test-stream, "Abcdefgh")
241     # . . push args
242     68/push  "Abcdefgh"/imm32
243     68/push  _test-stream/imm32
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     # pretend buffer is full
249     # . _test-buffered-file->read = 6  # >= _test-buffered-file->size
250     b8/copy-to-eax  _test-buffered-file/imm32
251     c7          0/subop/copy        1/mod/*+disp8   0/rm32/eax    .           .             .           .           8/disp8         6/imm32           # copy to *(eax+8)
252     # read-byte-buffered(_test-buffered-file)
253     # . . push args
254     68/push  _test-buffered-file/imm32
255     # . . call
256     e8/call  read-byte-buffered/disp32
257     # . . discard args
258     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
259     # check-ints-equal(eax, 'A', msg)
260     # . . push args
261     68/push  "F - test-read-byte-buffered-refills-buffer"/imm32
262     68/push  0x41/imm32
263     50/push-eax
264     # . . call
265     e8/call  check-ints-equal/disp32
266     # . . discard args
267     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
268     # . end
269     c3/return
271 # Return next byte value in eax, with top 3 bytes cleared.
272 # Abort on reaching end of stream.
273 read-byte:  # s: (addr stream byte) -> result/eax: byte
274     # . prologue
275     55/push-ebp
276     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
277     # . save registers
278     51/push-ecx
279     56/push-esi
280     # esi = s
281     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
282     # ecx = s->read
283     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
284     # if (f->read >= f->write) abort
285     3b/compare                      0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare ecx with *esi
286     0f 8d/jump-if->=  $read-byte:abort/disp32
287     # result = f->data[f->read]
288     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
289     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(esi+ecx+12) to AL
290     # ++f->read
291     ff          0/subop/increment   1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # increment *(esi+4)
292 $read-byte:end:
293     # . restore registers
294     5e/pop-to-esi
295     59/pop-to-ecx
296     # . epilogue
297     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
298     5d/pop-to-ebp
299     c3/return
301 $read-byte:abort:
302     # . _write(2/stderr, error)
303     # . . push args
304     68/push  "read-byte: empty stream\n"/imm32
305     68/push  2/imm32/stderr
306     # . . call
307     e8/call  _write/disp32
308     # . . discard args
309     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
310     # . syscall_exit(1)
311     bb/copy-to-ebx  1/imm32
312     e8/call  syscall_exit/disp32
313     # never gets here
315 == data
317 # a test buffered file for _test-stream
318 _test-buffered-file:  # buffered-file
319     # file descriptor or (addr stream byte)
320     _test-stream/imm32
321 $_test-buffered-file->buffer:
322     # current write index
323     0/imm32
324     # current read index
325     0/imm32
326     # size
327     6/imm32
328     # data
329     00 00 00 00 00 00  # 6 bytes
331 _test-input-stream:  # (stream byte)
332     # current write index
333     0/imm32
334     # current read index
335     0/imm32
336     # size
337     0x400/imm32  # 1024 bytes
338     # data (64 lines x 16 bytes/line)
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     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
358     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
359     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
360     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
361     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
362     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
363     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
364     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
365     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
366     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
367     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
368     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
369     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
370     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
371     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
372     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
373     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
374     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
375     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
376     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
377     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
378     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
379     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
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     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
389     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
390     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
391     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
392     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
393     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
394     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
395     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
396     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
397     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
398     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
399     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
400     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
401     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
402     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
404 # a test buffered file for _test-input-stream
405 _test-input-buffered-file:  # buffered-file
406     # file descriptor or (addr stream byte)
407     _test-input-stream/imm32
408 $_test-input-buffered-file->buffer:
409     # current write index
410     0/imm32
411     # current read index
412     0/imm32
413     # size
414     6/imm32
415     # data
416     00 00 00 00 00 00  # 6 bytes
418 # . . vim:nowrap:textwidth=0