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.
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
18 # inlined fields for a stream
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.
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
41 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
46 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi
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)
54 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy esi+4 to eax
57 e8/call clear-stream/disp32
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)
65 ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi
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
80 ff 0/subop/increment 1/mod/*+disp8 6/rm32/esi . . . . 8/disp8 . # increment *(esi+8)
81 $read-byte-buffered:end:
86 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
92 test-read-byte-buffered-single:
93 # - check that read-byte-buffered returns first byte of 'file'
95 # . clear-stream(_test-stream)
97 68/push _test-stream/imm32
99 e8/call clear-stream/disp32
101 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
102 # . clear-stream(_test-buffered-file->buffer)
104 68/push $_test-buffered-file->buffer/imm32
106 e8/call clear-stream/disp32
108 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
109 # . write(_test-stream, "Ab")
112 68/push _test-stream/imm32
116 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
117 # read-byte-buffered(_test-buffered-file)
119 68/push _test-buffered-file/imm32
121 e8/call read-byte-buffered/disp32
123 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
124 # check-ints-equal(eax, 'A', msg)
126 68/push "F - test-read-byte-buffered-single"/imm32
130 e8/call check-ints-equal/disp32
132 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
136 test-read-byte-buffered-multiple:
137 # - call read-byte-buffered twice, check that second call returns second byte
139 # . clear-stream(_test-stream)
141 68/push _test-stream/imm32
143 e8/call clear-stream/disp32
145 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
146 # . clear-stream($_test-buffered-file->buffer)
148 68/push $_test-buffered-file->buffer/imm32
150 e8/call clear-stream/disp32
152 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
153 # . write(_test-stream, "Ab")
156 68/push _test-stream/imm32
160 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
161 # read-byte-buffered(_test-buffered-file)
163 68/push _test-buffered-file/imm32
165 e8/call read-byte-buffered/disp32
167 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
168 # read-byte-buffered(_test-buffered-file)
170 68/push _test-buffered-file/imm32
172 e8/call read-byte-buffered/disp32
174 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
175 # check-ints-equal(eax, 'b', msg)
177 68/push "F - test-read-byte-buffered-multiple"/imm32
181 e8/call check-ints-equal/disp32
183 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
187 test-read-byte-buffered-end-of-file:
188 # - call read-byte-buffered on an empty 'file', check that it returns Eof
190 # . clear-stream(_test-stream)
192 68/push _test-stream/imm32
194 e8/call clear-stream/disp32
196 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
197 # . clear-stream($_test-buffered-file->buffer)
199 68/push $_test-buffered-file->buffer/imm32
201 e8/call clear-stream/disp32
203 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
204 # read-byte-buffered(_test-buffered-file)
206 68/push _test-buffered-file/imm32
208 e8/call read-byte-buffered/disp32
210 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
211 # check-ints-equal(eax, 0xffffffff, msg)
213 68/push "F - test-read-byte-buffered-end-of-file"/imm32
214 68/push 0xffffffff/imm32/Eof
217 e8/call check-ints-equal/disp32
219 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
223 test-read-byte-buffered-refills-buffer:
224 # - consume buffered-file's buffer, check that next read-byte-buffered still works
226 # . clear-stream(_test-stream)
228 68/push _test-stream/imm32
230 e8/call clear-stream/disp32
232 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
233 # . clear-stream($_test-buffered-file->buffer)
235 68/push $_test-buffered-file->buffer/imm32
237 e8/call clear-stream/disp32
239 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
240 # . write(_test-stream, "Abcdefgh")
242 68/push "Abcdefgh"/imm32
243 68/push _test-stream/imm32
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)
254 68/push _test-buffered-file/imm32
256 e8/call read-byte-buffered/disp32
258 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
259 # check-ints-equal(eax, 'A', msg)
261 68/push "F - test-read-byte-buffered-refills-buffer"/imm32
265 e8/call check-ints-equal/disp32
267 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
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
276 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
281 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi
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
291 ff 0/subop/increment 1/mod/*+disp8 6/rm32/esi . . . . 4/disp8 . # increment *(esi+4)
293 # . restore registers
297 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
302 # . _write(2/stderr, error)
304 68/push "read-byte: empty stream\n"/imm32
305 68/push 2/imm32/stderr
307 e8/call _write/disp32
309 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
311 bb/copy-to-ebx 1/imm32
312 e8/call syscall_exit/disp32
317 # a test buffered file for _test-stream
318 _test-buffered-file: # buffered-file
319 # file descriptor or (addr stream byte)
321 $_test-buffered-file->buffer:
322 # current write index
329 00 00 00 00 00 00 # 6 bytes
331 _test-input-stream: # (stream byte)
332 # current write index
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
416 00 00 00 00 00 00 # 6 bytes
418 # . . vim:nowrap:textwidth=0