mu/subx/056write.subx

212 lines
10 KiB
Plaintext
Raw Normal View History

# 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 length as usual
== code
# instruction effective address operand 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
2018-11-21 07:18:49 +00:00
# main:
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
# syscall(exit, Num-test-failures)
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
b8/copy-to-EAX 1/imm32
cd/syscall 0x80/imm8
2018-11-13 06:00:51 +00:00
write: # f : fd or (address stream), s : (address array byte) -> bytes_written/EAX
# (If we ever leave the Linux kernel behind, it may be better to return
# the number of bytes *not* written. Success would then be signaled by
# returning 0.)
# prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# if (f < 0x08000000) _write(f, s), return # f can't be a user-mode address, so treat it as a kernel file descriptor
81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 0x08000000/imm32 # compare *(EBP+8)
2018-11-18 04:45:22 +00:00
7d/jump-if-greater-or-equal $write:fake/disp8
# push args
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 . . 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:
2018-11-13 06:00:51 +00:00
# otherwise, treat 'f' as a stream to append to
# save registers
51/push-ECX
52/push-EDX
53/push-EBX
# ECX = f
8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 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.length
8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 3/r32/EBX 8/disp8 . # copy *(ECX+8) to EBX
2018-10-31 05:36:52 +00:00
# EAX = _append-3(&f.data[f.write], &f.data[f.length], s)
# push s
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]
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
2018-10-31 05:36:52 +00:00
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
$write:end:
# epilog
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
5d/pop-to-EBP
c3/return
2018-11-18 08:17:29 +00:00
clear-stream: # f : (address stream) -> <void>
# prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# save registers
50/push-EAX
51/push-ECX
# 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
# ECX = f.length
8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 8/disp8 . # copy *(EAX+8) to ECX
# ECX = &f.data[f.length]
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
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
81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 0xc/imm32 # add to EAX
# while (true)
$clear-stream:loop:
# if EAX >= ECX break
39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX
7d/jump-if-greater-or-equal $clear-stream:end/disp8
# *EAX = 0
c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX
# EAX += 4
81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 4/imm32 # add to EAX
eb/jump $clear-stream:loop/disp8
$clear-stream:end:
# restore registers
59/pop-to-ECX
58/pop-to-EAX
# epilog
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
5d/pop-to-EBP
c3/return
test-write-single:
2018-11-18 07:36:32 +00:00
# clear-stream(_test-stream)
# push args
2018-11-18 07:36:32 +00:00
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
2018-11-18 07:36:32 +00:00
# write(_test-stream, "Ab")
# push args
68/push "Ab"/imm32
2018-11-18 07:36:32 +00:00
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
2018-11-18 08:17:29 +00:00
# check-ints-equal(EAX, 2)
# push args
68/push "F - test-read-single: return EAX"/imm32
68/push 2/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
2018-11-18 07:36:32 +00:00
# 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
2018-11-18 07:36:32 +00:00
# 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:
2018-11-18 07:36:32 +00:00
# clear-stream(_test-stream)
# push args
2018-11-18 07:36:32 +00:00
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
2018-11-18 07:36:32 +00:00
# write(_test-stream, "C")
# push args
68/push "C"/imm32
2018-11-18 07:36:32 +00:00
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
2018-11-18 07:36:32 +00:00
# write(_test-stream, "D")
# push args
68/push "D"/imm32
2018-11-18 07:36:32 +00:00
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
2018-11-18 07:36:32 +00:00
# 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
2018-11-18 07:36:32 +00:00
# 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
2018-11-18 08:17:29 +00:00
== data
_test-stream:
# current write index
00 00 00 00
# current read index
00 00 00 00
# length (= 8)
08 00 00 00
# data
00 00 00 00 00 00 00 00 # 8 bytes