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.
160 lines
7.7 KiB
Plaintext
160 lines
7.7 KiB
Plaintext
# write: like _write, but also support in-memory streams in addition to file
|
|
# descriptors.
|
|
#
|
|
# Our first dependency-injected and testable primitive. We can pass it either
|
|
# a file descriptor or an address to a stream. If a file descriptor is passed
|
|
# in, we _write to it using the right syscall. If a 'fake file descriptor' or
|
|
# stream is passed in, we append to the stream. This lets us redirect output
|
|
# in tests and check it later.
|
|
#
|
|
# We assume our data segment will never begin at an address shorter than
|
|
# 0x08000000, so any smaller arguments are assumed to be real file descriptors.
|
|
#
|
|
# A stream looks like this:
|
|
# read: int # index at which to read next
|
|
# write: int # index at which writes go
|
|
# data: (array byte) # prefixed by size as usual
|
|
|
|
== 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
|
|
|
|
# TODO: come up with a way to signal when a write to disk fails
|
|
write: # f: fd or (addr stream byte), s: (addr array byte)
|
|
# . prologue
|
|
55/push-ebp
|
|
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
|
# if (f < 0x08000000) _write(f, s) and return # f can't be a user-mode address, so treat it as a kernel file descriptor
|
|
81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 0x08000000/imm32 # compare *(ebp+8)
|
|
73/jump-if-addr>= $write:fake/disp8
|
|
# . . push args
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
|
|
# . . call
|
|
e8/call _write/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
|
eb/jump $write:end/disp8
|
|
$write:fake:
|
|
# otherwise, treat 'f' as a stream to append to
|
|
# . save registers
|
|
50/push-eax
|
|
51/push-ecx
|
|
52/push-edx
|
|
53/push-ebx
|
|
# ecx = f
|
|
8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx
|
|
# edx = f->write
|
|
8b/copy 0/mod/indirect 1/rm32/ecx . . . 2/r32/edx . . # copy *ecx to edx
|
|
# ebx = f->size
|
|
8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 3/r32/ebx 8/disp8 . # copy *(ecx+8) to ebx
|
|
# eax = _append-3(&f->data[f->write], &f->data[f->size], s)
|
|
# . . push s
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
|
|
# . . push &f->data[f->size]
|
|
8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ecx 3/index/ebx . 3/r32/ebx 0xc/disp8 . # copy ecx+ebx+12 to ebx
|
|
53/push-ebx
|
|
# . . push &f->data[f->write]
|
|
8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ecx 2/index/edx . 3/r32/ebx 0xc/disp8 . # copy ecx+edx+12 to ebx
|
|
53/push-ebx
|
|
# . . call
|
|
e8/call _append-3/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
|
|
# f->write += eax
|
|
01/add 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # add eax to *ecx
|
|
# . restore registers
|
|
5b/pop-to-ebx
|
|
5a/pop-to-edx
|
|
59/pop-to-ecx
|
|
58/pop-to-eax
|
|
$write:end:
|
|
# . epilogue
|
|
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
|
5d/pop-to-ebp
|
|
c3/return
|
|
|
|
test-write-single:
|
|
# 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
|
|
# 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
|
|
# check-ints-equal(*_test-stream->data, 41/A 62/b 00 00, msg)
|
|
# . . push args
|
|
68/push "F - test-write-single"/imm32
|
|
68/push 0x006241/imm32/Ab
|
|
# . . push *_test-stream->data
|
|
b8/copy-to-eax _test-stream/imm32
|
|
ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 0xc/disp8 . # push *(eax+12)
|
|
# . . 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-write-appends:
|
|
# 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
|
|
# write(_test-stream, "C")
|
|
# . . push args
|
|
68/push "C"/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
|
|
# write(_test-stream, "D")
|
|
# . . push args
|
|
68/push "D"/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
|
|
# check-ints-equal(*_test-stream->data, 43/C 44/D 00 00, msg)
|
|
# . . push args
|
|
68/push "F - test-write-appends"/imm32
|
|
68/push 0x00004443/imm32/C-D
|
|
# . . push *_test-stream->data
|
|
b8/copy-to-eax _test-stream/imm32
|
|
ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 0xc/disp8 . # push *(eax+12)
|
|
# . . 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
|
|
|
|
_test-stream: # (stream byte)
|
|
# current write index
|
|
0/imm32
|
|
# current read index
|
|
0/imm32
|
|
# size
|
|
0x10/imm32
|
|
# data (2 lines x 8 bytes/line)
|
|
00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00
|
|
|
|
# . . vim:nowrap:textwidth=0
|