about summary refs log tree commit diff stats
path: root/linux/122read-line.subx
diff options
context:
space:
mode:
Diffstat (limited to 'linux/122read-line.subx')
-rw-r--r--linux/122read-line.subx385
1 files changed, 385 insertions, 0 deletions
diff --git a/linux/122read-line.subx b/linux/122read-line.subx
new file mode 100644
index 00000000..02dcc0fd
--- /dev/null
+++ b/linux/122read-line.subx
@@ -0,0 +1,385 @@
+== 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
+
+# read bytes from 'f' until (and including) a newline and store them into 's'
+# 's' fails to grow if and only if no data found
+# just abort if 's' is too small
+read-line-buffered:  # f: (addr buffered-file), s: (addr stream byte)
+    # pseudocode:
+    #   while true
+    #     if (s->write >= s->size) abort
+    #     if (f->read >= f->write) populate stream from file
+    #     if (f->write == 0) break
+    #     AL = f->data[f->read]
+    #     s->data[s->write] = AL
+    #     ++f->read
+    #     ++s->write
+    #     if (AL == '\n') break
+    # . prologue
+    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
+    56/push-esi
+    57/push-edi
+    # esi = f
+    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
+    # ecx = f->read
+    8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(esi+8) to ecx
+    # edi = s
+    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
+    # edx = s->write
+    8b/copy                         0/mod/indirect  7/rm32/edi    .           .             .           2/r32/edx   .               .                 # copy *edi to edx
+$read-line-buffered:loop:
+    # if (s->write >= s->size) abort
+    3b/compare                      1/mod/*+disp8   7/rm32/edi    .           .             .           2/r32/edx   8/disp8         .                 # compare edx with *(edi+8)
+    7d/jump-if->=  $read-line-buffered:abort/disp8
+    # if (f->read >= f->write) populate stream from file
+    3b/compare                      1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # compare ecx with *(esi+4)
+    7c/jump-if-<  $read-line-buffered:from-stream/disp8
+    # . clear-stream(stream = f+4)
+    # . . push args
+    8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy esi+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->read must now be 0; update its cache at ecx
+    31/xor                          3/mod/direct    1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # clear ecx
+    # . eax = read(f->fd, stream = f+4)
+    # . . push args
+    50/push-eax
+    ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
+    # . . call
+    e8/call  read/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # if (f->write == 0) break
+    # since f->read was initially 0, eax is the same as f->write
+    # . if (eax == 0) return true
+    3d/compare-eax-and  0/imm32
+    74/jump-if-=  $read-line-buffered:end/disp8
+$read-line-buffered:from-stream:
+    # AL = f->data[f->read]
+    31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
+    8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0x10/disp8      .                 # copy byte at *(esi+ecx+16) to AL
+    # s->data[s->write] = AL
+    88/copy-byte                    1/mod/*+disp8   4/rm32/sib    7/base/edi  2/index/edx   .           0/r32/AL    0xc/disp8       .                 # copy AL to *(edi+edx+12)
+    # ++f->read
+    41/increment-ecx
+    # ++s->write
+    42/increment-edx
+    # if (AL == '\n') return
+    3d/compare-eax-and  0xa/imm32/newline
+    75/jump-if-!=  $read-line-buffered:loop/disp8
+$read-line-buffered:end:
+    # save f->read
+    89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   8/disp8         .                 # copy ecx to *(esi+8)
+    # save s->write
+    89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           2/r32/edx   .               .                 # copy edx to *edi
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+$read-line-buffered:abort:
+    # . _write(2/stderr, error)
+    # . . push args
+    68/push  "read-line-buffered: line too long\n"/imm32
+    68/push  2/imm32/stderr
+    # . . call
+    e8/call  _write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . syscall(exit, 1)
+    bb/copy-to-ebx  1/imm32
+    e8/call  syscall_exit/disp32
+    # never gets here
+
+test-read-line-buffered:
+    # - check that read-line-buffered stops at a newline
+    # 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->buffer)
+    # . . push args
+    68/push  $_test-buffered-file->buffer/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-tmp-stream)
+    # . . push args
+    68/push  _test-tmp-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\ncd")
+    # . . push args
+    68/push  "ab\ncd"/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
+    # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream
+    # . eax = read-line-buffered(_test-buffered-file, _test-tmp-stream)
+    # . . push args
+    68/push  _test-tmp-stream/imm32
+    68/push  _test-buffered-file/imm32
+    # . . call
+    e8/call  read-line-buffered/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # check-next-stream-line-equal(_test-tmp-stream, "ab", msg)
+    # . . push args
+    68/push  "F - test-read-line-buffered"/imm32
+    68/push  "ab"/imm32
+    68/push  _test-tmp-stream/imm32
+    # . . call
+    e8/call  check-next-stream-line-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # end
+    c3/return
+
+test-read-line-buffered-reads-final-line-until-Eof:
+    # 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->buffer)
+    # . . push args
+    68/push  $_test-buffered-file->buffer/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-tmp-stream)
+    # . . push args
+    68/push  _test-tmp-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, "cd")
+    # . . push args
+    68/push  "cd"/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
+    # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream
+    # . eax = read-line-buffered(_test-buffered-file, _test-tmp-stream)
+    # . . push args
+    68/push  _test-tmp-stream/imm32
+    68/push  _test-buffered-file/imm32
+    # . . call
+    e8/call  read-line-buffered/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # check-stream-equal(_test-tmp-stream, "cd", msg)
+    # . . push args
+    68/push  "F - test-read-line-buffered-reads-final-line-until-Eof"/imm32
+    68/push  "cd"/imm32
+    68/push  _test-tmp-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
+
+# read bytes from 'f' until (and including) a newline and store them into 's'
+# 's' fails to grow if and only if no data found
+# just abort if 's' is too small
+read-line:  # f: (addr stream byte), s: (addr stream byte)
+    # pseudocode:
+    #   while true
+    #     if (s->write >= s->size) abort
+    #     if (f->read >= f->write) break
+    #     AL = f->data[f->read]
+    #     s->data[s->write] = AL
+    #     ++f->read
+    #     ++s->write
+    #     if (AL == '\n') break
+    # . prologue
+    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
+    56/push-esi
+    57/push-edi
+    # esi = f
+    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
+    # ecx = f->read
+    8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
+    # edi = s
+    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
+    # edx = s->write
+    8b/copy                         0/mod/indirect  7/rm32/edi    .           .             .           2/r32/edx   .               .                 # copy *edi to edx
+$read-line:loop:
+    # if (s->write >= s->size) abort
+    3b/compare                      1/mod/*+disp8   7/rm32/edi    .           .             .           2/r32/edx   8/disp8         .                 # compare edx with *(edi+8)
+    0f 8d/jump-if->=  $read-line:abort/disp32
+    # if (f->read >= f->write) break
+    3b/compare                      0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare ecx with *esi
+    7d/jump-if->=  $read-line:end/disp8
+    # AL = f->data[f->read]
+    31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
+    8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(esi+ecx+12) to AL
+    # s->data[s->write] = AL
+    88/copy-byte                    1/mod/*+disp8   4/rm32/sib    7/base/edi  2/index/edx   .           0/r32/AL    0xc/disp8       .                 # copy AL to *(edi+edx+12)
+    # ++f->read
+    41/increment-ecx
+    # ++s->write
+    42/increment-edx
+    # if (AL == '\n') return
+    3d/compare-eax-and  0xa/imm32/newline
+    0f 85/jump-if-!=  $read-line:loop/disp32
+$read-line:end:
+    # save f->read
+    89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy ecx to *(esi+4)
+    # save s->write
+    89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           2/r32/edx   .               .                 # copy edx to *edi
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+$read-line:abort:
+    # . _write(2/stderr, error)
+    # . . push args
+    68/push  "read-line: line too long\n"/imm32
+    68/push  2/imm32/stderr
+    # . . call
+    e8/call  _write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . syscall(exit, 1)
+    bb/copy-to-ebx  1/imm32
+    e8/call  syscall_exit/disp32
+    # never gets here
+
+test-read-line:
+    # - check that read-line stops at a newline
+    # 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-tmp-stream)
+    # . . push args
+    68/push  _test-tmp-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\ncd")
+    # . . push args
+    68/push  "ab\ncd"/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
+    # read a line from _test-stream into _test-tmp-stream
+    # . eax = read-line(_test-stream, _test-tmp-stream)
+    # . . push args
+    68/push  _test-tmp-stream/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  read-line/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # check-next-stream-line-equal(_test-tmp-stream, "ab", msg)
+    # . . push args
+    68/push  "F - test-read-line"/imm32
+    68/push  "ab"/imm32
+    68/push  _test-tmp-stream/imm32
+    # . . call
+    e8/call  check-next-stream-line-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # end
+    c3/return
+
+test-read-line-reads-final-line-until-Eof:
+    # 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-tmp-stream)
+    # . . push args
+    68/push  _test-tmp-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, "cd")
+    # . . push args
+    68/push  "cd"/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
+    # read a line from _test-stream into _test-tmp-stream
+    # . eax = read-line(_test-stream, _test-tmp-stream)
+    # . . push args
+    68/push  _test-tmp-stream/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  read-line/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # check-stream-equal(_test-tmp-stream, "cd", msg)
+    # . . push args
+    68/push  "F - test-read-line-reads-final-line-until-Eof"/imm32
+    68/push  "cd"/imm32
+    68/push  _test-tmp-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
+
+# . . vim:nowrap:textwidth=0
ef='#n936'>936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000