bfcc0f858a
So far it's unclear how to do this in a series of small commits. Still nibbling around the edges. In this commit we standardize some terminology: The length of an array or stream is denominated in the high-level elements. The _size_ is denominated in bytes. The thing we encode into the type is always the size, not the length. There's still an open question of what to do about the Mu `length` operator. I'd like to modify it to provide the length. Currently it provides the size. If I can't fix that I'll rename it.
327 lines
14 KiB
Plaintext
327 lines
14 KiB
Plaintext
# read-byte-buffered: one higher-level abstraction atop 'read'.
|
|
#
|
|
# There are many situations where 'read' is a lot to manage, and we need
|
|
# to abstract some details away. One of them is when we want to read a file
|
|
# character by character. In this situation we follow C's FILE data structure,
|
|
# which manages the underlying file descriptor together with the buffer it
|
|
# reads into. We call our version 'buffered-file'. Should be useful with other
|
|
# primitives as well, in later layers.
|
|
|
|
== data
|
|
|
|
# The buffered file for standard input. Also illustrates the layout for
|
|
# buffered-file: a pointer to the backing store, followed by a 'buffer' stream
|
|
Stdin: # buffered-file
|
|
# file descriptor or (addr stream byte)
|
|
0/imm32 # standard input
|
|
$Stdin->buffer:
|
|
# inlined fields for a stream
|
|
# current write index
|
|
0/imm32
|
|
# current read index
|
|
0/imm32
|
|
# size
|
|
8/imm32
|
|
# data
|
|
00 00 00 00 00 00 00 00 # 8 bytes
|
|
|
|
# TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But
|
|
# I don't want to type in 1024 bytes here.
|
|
|
|
== code
|
|
# instruction effective address register displacement immediate
|
|
# . op subop mod rm32 base index scale r32
|
|
# . 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
|
|
|
|
# return next byte value in eax, with top 3 bytes cleared.
|
|
# On reaching end of file, return 0xffffffff (Eof).
|
|
read-byte-buffered: # f: (addr buffered-file) -> byte-or-Eof/eax
|
|
# . prologue
|
|
55/push-ebp
|
|
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
|
# . save registers
|
|
51/push-ecx
|
|
56/push-esi
|
|
# esi = f
|
|
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi
|
|
# ecx = f->read
|
|
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 8/disp8 . # copy *(esi+8) to ecx
|
|
# if (f->read >= f->write) populate stream from file
|
|
3b/compare 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # compare ecx with *(esi+4)
|
|
7c/jump-if-< $read-byte-buffered:from-stream/disp8
|
|
# . clear-stream(stream = f+4)
|
|
# . . push args
|
|
8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy esi+4 to eax
|
|
50/push-eax
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
|
# . f->read must now be 0; update its cache at ecx
|
|
31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx
|
|
# . eax = read(f->fd, stream = f+4)
|
|
# . . push args
|
|
50/push-eax
|
|
ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi
|
|
# . . call
|
|
e8/call read/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
|
# if (eax == 0) return 0xffffffff
|
|
3d/compare-eax-and 0/imm32
|
|
75/jump-if-!= $read-byte-buffered:from-stream/disp8
|
|
b8/copy-to-eax 0xffffffff/imm32/Eof
|
|
eb/jump $read-byte-buffered:end/disp8
|
|
$read-byte-buffered:from-stream:
|
|
# byte-or-Eof = f->data[f->read]
|
|
31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax
|
|
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
|
|
# ++f->read
|
|
ff 0/subop/increment 1/mod/*+disp8 6/rm32/esi . . . . 8/disp8 . # increment *(esi+8)
|
|
$read-byte-buffered:end:
|
|
# . restore registers
|
|
5e/pop-to-esi
|
|
59/pop-to-ecx
|
|
# . epilogue
|
|
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
|
5d/pop-to-ebp
|
|
c3/return
|
|
|
|
# - tests
|
|
|
|
test-read-byte-buffered-single:
|
|
# - check that read-byte-buffered returns first byte of 'file'
|
|
# setup
|
|
# . clear-stream(_test-stream)
|
|
# . . push args
|
|
68/push _test-stream/imm32
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
|
# . clear-stream(_test-buffered-file->buffer)
|
|
# . . push args
|
|
68/push $_test-buffered-file->buffer/imm32
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
|
# . write(_test-stream, "Ab")
|
|
# . . push args
|
|
68/push "Ab"/imm32
|
|
68/push _test-stream/imm32
|
|
# . . call
|
|
e8/call write/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
|
# read-byte-buffered(_test-buffered-file)
|
|
# . . push args
|
|
68/push _test-buffered-file/imm32
|
|
# . . call
|
|
e8/call read-byte-buffered/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
|
# check-ints-equal(eax, 'A', msg)
|
|
# . . push args
|
|
68/push "F - test-read-byte-buffered-single"/imm32
|
|
68/push 0x41/imm32
|
|
50/push-eax
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
|
|
# . end
|
|
c3/return
|
|
|
|
test-read-byte-buffered-multiple:
|
|
# - call read-byte-buffered twice, check that second call returns second byte
|
|
# setup
|
|
# . clear-stream(_test-stream)
|
|
# . . push args
|
|
68/push _test-stream/imm32
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
|
# . clear-stream($_test-buffered-file->buffer)
|
|
# . . push args
|
|
68/push $_test-buffered-file->buffer/imm32
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
|
# . write(_test-stream, "Ab")
|
|
# . . push args
|
|
68/push "Ab"/imm32
|
|
68/push _test-stream/imm32
|
|
# . . call
|
|
e8/call write/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
|
# read-byte-buffered(_test-buffered-file)
|
|
# . . push args
|
|
68/push _test-buffered-file/imm32
|
|
# . . call
|
|
e8/call read-byte-buffered/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
|
# read-byte-buffered(_test-buffered-file)
|
|
# . . push args
|
|
68/push _test-buffered-file/imm32
|
|
# . . call
|
|
e8/call read-byte-buffered/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
|
# check-ints-equal(eax, 'b', msg)
|
|
# . . push args
|
|
68/push "F - test-read-byte-buffered-multiple"/imm32
|
|
68/push 0x62/imm32
|
|
50/push-eax
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
|
|
# . end
|
|
c3/return
|
|
|
|
test-read-byte-buffered-end-of-file:
|
|
# - call read-byte-buffered on an empty 'file', check that it returns Eof
|
|
# setup
|
|
# . clear-stream(_test-stream)
|
|
# . . push args
|
|
68/push _test-stream/imm32
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
|
# . clear-stream($_test-buffered-file->buffer)
|
|
# . . push args
|
|
68/push $_test-buffered-file->buffer/imm32
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
|
# read-byte-buffered(_test-buffered-file)
|
|
# . . push args
|
|
68/push _test-buffered-file/imm32
|
|
# . . call
|
|
e8/call read-byte-buffered/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
|
# check-ints-equal(eax, 0xffffffff, msg)
|
|
# . . push args
|
|
68/push "F - test-read-byte-buffered-end-of-file"/imm32
|
|
68/push 0xffffffff/imm32/Eof
|
|
50/push-eax
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
|
|
# . end
|
|
c3/return
|
|
|
|
test-read-byte-buffered-refills-buffer:
|
|
# - consume buffered-file's buffer, check that next read-byte-buffered still works
|
|
# setup
|
|
# . clear-stream(_test-stream)
|
|
# . . push args
|
|
68/push _test-stream/imm32
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
|
# . clear-stream($_test-buffered-file->buffer)
|
|
# . . push args
|
|
68/push $_test-buffered-file->buffer/imm32
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
|
# . write(_test-stream, "Abcdefgh")
|
|
# . . push args
|
|
68/push "Abcdefgh"/imm32
|
|
68/push _test-stream/imm32
|
|
# . . call
|
|
e8/call write/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
|
# pretend buffer is full
|
|
# . _test-buffered-file->read = 6 # >= _test-buffered-file->size
|
|
b8/copy-to-eax _test-buffered-file/imm32
|
|
c7 0/subop/copy 1/mod/*+disp8 0/rm32/eax . . . . 8/disp8 6/imm32 # copy to *(eax+8)
|
|
# read-byte-buffered(_test-buffered-file)
|
|
# . . push args
|
|
68/push _test-buffered-file/imm32
|
|
# . . call
|
|
e8/call read-byte-buffered/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
|
# check-ints-equal(eax, 'A', msg)
|
|
# . . push args
|
|
68/push "F - test-read-byte-buffered-refills-buffer"/imm32
|
|
68/push 0x41/imm32
|
|
50/push-eax
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
|
|
# . end
|
|
c3/return
|
|
|
|
== data
|
|
|
|
# a test buffered file for _test-stream
|
|
_test-buffered-file: # buffered-file
|
|
# file descriptor or (addr stream byte)
|
|
_test-stream/imm32
|
|
$_test-buffered-file->buffer:
|
|
# current write index
|
|
0/imm32
|
|
# current read index
|
|
0/imm32
|
|
# size
|
|
6/imm32
|
|
# data
|
|
00 00 00 00 00 00 # 6 bytes
|
|
|
|
_test-input-stream: # (stream byte)
|
|
# current write index
|
|
0/imm32
|
|
# current read index
|
|
0/imm32
|
|
# size
|
|
0x100/imm32 # 256 bytes
|
|
# data (16 lines x 16 bytes/line)
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
|
|
# a test buffered file for _test-input-stream
|
|
_test-input-buffered-file: # buffered-file
|
|
# file descriptor or (addr stream byte)
|
|
_test-input-stream/imm32
|
|
$_test-input-buffered-file->buffer:
|
|
# current write index
|
|
0/imm32
|
|
# current read index
|
|
0/imm32
|
|
# size
|
|
6/imm32
|
|
# data
|
|
00 00 00 00 00 00 # 6 bytes
|
|
|
|
# . . vim:nowrap:textwidth=0
|