We'll use a common stream data structure for input and output streams.

Having separate types makes more sense in a more high-level language, where
we have type checking and where functions for handling the different types
are more concise. But in machine code the sweet spot is more toward fewer
types.
This commit is contained in:
Kartik Agaram 2018-10-29 16:22:40 -07:00
parent 3d94e2db0e
commit f94442fe2d
3 changed files with 48 additions and 43 deletions

View File

@ -1,26 +1,29 @@
# write: like _write, but also support in-memory output streams (`ostream`s) # write: like _write, but also support in-memory streams in addition to file
# in addition to file descriptors. # descriptors.
# #
# Our first dependency-injected and testable primitive. We can pass it either # Our first dependency-injected and testable primitive. We can pass it either
# a file descriptor or an address to an ostream. If a file descriptor is # a file descriptor or an address to a stream. If a file descriptor is passed
# passed in, we _write to it using the right syscall. If a 'fake file descriptor' # in, we _write to it using the right syscall. If a 'fake file descriptor' or
# or ostream is passed in, we append to the ostream. This lets us redirect # stream is passed in, we append to the stream. This lets us redirect output
# output in tests and check it later. # in tests and check it later.
# #
# We assume our data segment will never begin at an address shorter than # 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. # 0x08000000, so any smaller arguments are assumed to be real file descriptors.
# #
# An ostream looks like this: # A stream looks like this:
# read: int # index at which to read next
# write: int # index at which writes go # write: int # index at which writes go
# data: (array byte) # prefixed by length as usual # data: (array byte) # prefixed by length as usual
== data == data
# In-memory ostream for tests to write() to. # In-memory stream for tests to write() to.
# Also illustrates the layout of ostreams. # Also illustrates the layout of streams.
Test-ostream: Test-stream:
# current write index # current write index
00 00 00 00 00 00 00 00
# current read index
00 00 00 00
# length (= 8) # length (= 8)
08 00 00 00 08 00 00 00
# data # data
@ -39,7 +42,7 @@ Test-ostream:
b8/copy-to-EAX 1/imm32 b8/copy-to-EAX 1/imm32
cd/syscall 0x80/imm8 cd/syscall 0x80/imm8
write: # f : fd or (address ostream), s : (address array byte) -> <void> write: # f : fd or (address stream), s : (address array byte) -> <void>
# prolog # prolog
55/push-EBP 55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
@ -55,7 +58,7 @@ write: # f : fd or (address ostream), s : (address array byte) -> <void>
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
eb/jump $write:end/disp8 eb/jump $write:end/disp8
$write:fake: $write:fake:
# otherwise, treat 'f' as an ostream to append to # otherwise, treat 'f' as an stream to append to
# save registers # save registers
50/push-EAX 50/push-EAX
51/push-ECX 51/push-ECX
@ -66,15 +69,15 @@ $write:fake:
# EDX = f.write # EDX = f.write
8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX
# EBX = f.length # EBX = f.length
8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 3/r32/EBX 4/disp8 . # copy *(ECX+4) to EBX 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 3/r32/EBX 8/disp8 . # copy *(ECX+8) to EBX
# EAX = _append(&f.data[f.write], &f.data[f.length], s) # EAX = _append(&f.data[f.write], &f.data[f.length], s)
# push s # push s
ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12)
# push &f.data[f.length] # push &f.data[f.length]
8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 3/index/EBX . 3/r32/EBX 8/disp8 . # copy ECX+EBX+8 to EBX 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 53/push-EBX
# push &f.data[f.write] # push &f.data[f.write]
8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 2/index/EDX . 3/r32/EBX 8/disp8 . # copy ECX+EBX+8 to EBX 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 53/push-EBX
# call # call
e8/call _append/disp32 e8/call _append/disp32
@ -93,7 +96,7 @@ $write:end:
5d/pop-to-EBP 5d/pop-to-EBP
c3/return c3/return
clear-ostream: # f : (address ostream) clear-stream: # f : (address stream)
# prolog # prolog
55/push-EBP 55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
@ -103,24 +106,26 @@ clear-ostream: # f : (address ostream)
# EAX = f # EAX = f
8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX
# ECX = f.length # ECX = f.length
8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy *(EAX+4) to ECX 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 8/disp8 . # copy *(EAX+8) to ECX
# ECX = &f.data[f.length] # ECX = &f.data[f.length]
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 8/disp8 . # copy EAX+ECX+8 to ECX 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 0xc/disp8 . # copy EAX+ECX+12 to ECX
# f.write = 0 # f.write = 0
c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX
# f.read = 0
c7/copy 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 0/imm32 # copy to *(EAX+4)
# EAX = f.data # EAX = f.data
81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 8/imm32 # add to EAX 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 0xc/imm32 # add to EAX
# while (true) # while (true)
$clear-ostream:loop: $clear-stream:loop:
# if EAX >= ECX break # if EAX >= ECX break
39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX
7d/jump-if-greater-or-equal $clear-ostream:end/disp8 7d/jump-if-greater-or-equal $clear-stream:end/disp8
# *EAX = 0 # *EAX = 0
c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX
# EAX += 4 # EAX += 4
81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 4/imm32 # add to EAX 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 4/imm32 # add to EAX
eb/jump $clear-ostream:loop/disp8 eb/jump $clear-stream:loop/disp8
$clear-ostream:end: $clear-stream:end:
# restore registers # restore registers
59/pop-to-ECX 59/pop-to-ECX
58/pop-to-EAX 58/pop-to-EAX
@ -130,28 +135,28 @@ $clear-ostream:end:
c3/return c3/return
test-write-single: test-write-single:
# clear-ostream(Test-ostream) # clear-stream(Test-stream)
# push args # push args
68/push Test-ostream/imm32 68/push Test-stream/imm32
# call # call
e8/call clear-ostream/disp32 e8/call clear-stream/disp32
# discard args # discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# write(Test-ostream, "Ab") # write(Test-stream, "Ab")
# push args # push args
68/push "Ab"/imm32 68/push "Ab"/imm32
68/push Test-ostream/imm32 68/push Test-stream/imm32
# call # call
e8/call write/disp32 e8/call write/disp32
# discard args # discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# check-ints-equal(*Test-ostream.data, 41/A 62/b 00 00, msg) # check-ints-equal(*Test-stream.data, 41/A 62/b 00 00, msg)
# push args # push args
68/push "F - test-write-single"/imm32 68/push "F - test-write-single"/imm32
68/push 0x006241/imm32/Ab 68/push 0x006241/imm32/Ab
# push *Test-ostream.data # push *Test-stream.data
b8/copy-to-EAX Test-ostream/imm32 b8/copy-to-EAX Test-stream/imm32
ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 8/disp8 . # push *(EAX+8) ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12)
# call # call
e8/call check-ints-equal/disp32 e8/call check-ints-equal/disp32
# discard args # discard args
@ -160,36 +165,36 @@ test-write-single:
c3/return c3/return
test-write-appends: test-write-appends:
# clear-ostream(Test-ostream) # clear-stream(Test-stream)
# push args # push args
68/push Test-ostream/imm32 68/push Test-stream/imm32
# call # call
e8/call clear-ostream/disp32 e8/call clear-stream/disp32
# discard args # discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# write(Test-ostream, "C") # write(Test-stream, "C")
# push args # push args
68/push "C"/imm32 68/push "C"/imm32
68/push Test-ostream/imm32 68/push Test-stream/imm32
# call # call
e8/call write/disp32 e8/call write/disp32
# discard args # discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# write(Test-ostream, "D") # write(Test-stream, "D")
# push args # push args
68/push "D"/imm32 68/push "D"/imm32
68/push Test-ostream/imm32 68/push Test-stream/imm32
# call # call
e8/call write/disp32 e8/call write/disp32
# discard args # discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# check-ints-equal(*Test-ostream.data, 43/C 44/D 00 00, msg) # check-ints-equal(*Test-stream.data, 43/C 44/D 00 00, msg)
# push args # push args
68/push "F - test-write-appends"/imm32 68/push "F - test-write-appends"/imm32
68/push 0x00004443/imm32/C-D 68/push 0x00004443/imm32/C-D
# push *Test-ostream.data # push *Test-stream.data
b8/copy-to-EAX Test-ostream/imm32 b8/copy-to-EAX Test-stream/imm32
ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 8/disp8 . # push *(EAX+8) ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12)
# call # call
e8/call check-ints-equal/disp32 e8/call check-ints-equal/disp32
# discard args # discard args

Binary file not shown.

Binary file not shown.