mu/065write-buffered.subx

229 lines
11 KiB
Plaintext

# write-buffered: like 'write', but for a buffered-file
== 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
write-buffered: # f : (address buffered-file), msg : (address array byte) -> <void>
# pseudocode:
# in = msg->data
# inend = &msg->data[msg->length]
# while (in < inend)
# if f->write >= f->length
# flush(f)
# clear-stream(f)
# c = *in
# f->data[f->write] = c
# ++f->write
# ++in
#
# registers:
# in: ESI
# inend: ECX
# f: EDI
# f->length: EDX
# f->write: EBX (cached; need to keep in sync)
# c: EAX
#
# . 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
52/push-EDX
53/push-EBX
56/push-ESI
57/push-EDI
# EAX = msg
8b/copy 1/mod/*+disp8 5/rm32/EBP . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX
# in/ESI = msg->data
8d/copy-address 1/mod/*+disp8 0/rm32/EAX . . . 6/r32/ESI 4/disp8 . # copy EAX+4 to ESI
# inend/ECX = &msg->data[msg->length]
8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX
8d/copy-address 0/mod/indirect 4/rm32/sib 6/base/ESI 1/index/ECX . 1/r32/ECX . . # copy ESI+ECX to ECX
# EDI = f
8b/copy 1/mod/*+disp8 5/rm32/EBP . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI
# EDX = f->length
8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 0xc/disp8 . # copy *(EDI+12) to EDX
# EBX = f->write
8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy *(EDI+4) to EBX
$write-buffered:loop:
# if (in >= inend) break
39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX
73/jump-if-greater-or-equal-unsigned $write-buffered:loop-end/disp8
# if (f->write >= f->length) flush and clear f's stream
39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX with EDX
7c/jump-if-lesser $write-buffered:to-stream/disp8
# . persist f->write
89/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy EBX to *(EDI+4)
# . flush(f)
# . . push args
57/push-EDI
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# . clear-stream(stream = f+4)
# . . push args
8d/copy-address 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EDI+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->write must now be 0; update its cache at EBX
31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX
$write-buffered:to-stream:
# write to stream
# f->data[f->write] = *in
# . AL = *in
31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX
8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL
# . f->data[f->write] = AL
88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 3/index/EBX . 0/r32/AL 0x10/disp8 . # copy AL to *(EDI+EBX+16)
# ++f->write
43/increment-EBX
# ++in
46/increment-ESI
eb/jump $write-buffered:loop/disp8
$write-buffered:loop-end:
# persist necessary variables from registers
89/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy EBX to *(EDI+4)
$write-buffered:end:
# . restore registers
5f/pop-to-EDI
5e/pop-to-ESI
5b/pop-to-EBX
5a/pop-to-EDX
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-buffered:
# - check that write-buffered writes to the 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+4)
# . . push args
b8/copy-to-EAX _test-buffered-file/imm32
05/add-to-EAX 4/imm32
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
# write-buffered(_test-buffered-file, "Abc")
# . . push args
68/push "Abc"/imm32
68/push _test-buffered-file/imm32
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# flush(_test-buffered-file)
# . . push args
68/push _test-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# check-stream-equal(_test-stream, "Abc", msg)
# . . push args
68/push "F - test-write-buffered-single"/imm32
68/push "Abc"/imm32
68/push _test-stream/imm32
# . . call
e8/call check-stream-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
# . end
c3/return
test-write-buffered-with-intermediate-flush:
# - check that write-buffered flushes in the middle if its buffer fills up
# 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+4)
# . . push args
b8/copy-to-EAX _test-buffered-file/imm32
05/add-to-EAX 4/imm32
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
# _test-stream can hold 8 bytes, but _test-buffered-file can hold only 6.
# Try to write 7 bytes.
# . write-buffered(_test-buffered-file, "Abcdefg")
# . . push args
68/push "Abcdefg"/imm32
68/push _test-buffered-file/imm32
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# don't flush
# 6 bytes should still have gotten to _test-stream
# . check-ints-equal(*_test-stream->write, 6, msg)
# . . push args
68/push "F - test-write-buffered-with-intermediate-flush: flushed data"/imm32
68/push 6/imm32
# . . push *_test-stream->write
b8/copy-to-EAX _test-stream/imm32
ff 6/subop/push 0/mod/indirect 0/rm32/EAX . . . . . . # 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
# and 1 byte should still be in _test-buffered-file
# . check-ints-equal(*_test-buffered-file->write, 1, msg)
# . . push args
68/push "F - test-write-buffered-with-intermediate-flush: unflushed bytes"/imm32
68/push 1/imm32
# . . push *_test-buffered-file->write
b8/copy-to-EAX _test-buffered-file/imm32
ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4)
# . . 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
# The buffered file for standard error.
Stderr:
# file descriptor or (address stream)
2/imm32 # standard error
# current write index
0/imm32
# current read index
0/imm32
# length
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.
# . . vim:nowrap:textwidth=0