mu/057write.subx
Kartik Agaram bfcc0f858a 6182 - start of support for safe handles
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.
2020-04-03 12:35:53 -07:00

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