# 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) -> # 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