From 3350c34a74844e21ea69077e01efff3bae64bdcd Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Tue, 23 Mar 2021 17:31:08 -0700 Subject: . --- html/linux/112read-byte.subx.html | 484 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 484 insertions(+) create mode 100644 html/linux/112read-byte.subx.html (limited to 'html/linux/112read-byte.subx.html') diff --git a/html/linux/112read-byte.subx.html b/html/linux/112read-byte.subx.html new file mode 100644 index 00000000..db00e159 --- /dev/null +++ b/html/linux/112read-byte.subx.html @@ -0,0 +1,484 @@ + + + + +Mu - linux/112read-byte.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/112read-byte.subx +
+  1 # read-byte-buffered: one higher-level abstraction atop 'read'.
+  2 #
+  3 # There are many situations where 'read' is a lot to manage, and we need
+  4 # to abstract some details away. One of them is when we want to read a file
+  5 # character by character. In this situation we follow C's FILE data structure,
+  6 # which manages the underlying file descriptor together with the buffer it
+  7 # reads into. We call our version 'buffered-file'. Should be useful with other
+  8 # primitives as well, in later layers.
+  9 
+ 10 == data
+ 11 
+ 12 # The buffered file for standard input. Also illustrates the layout for
+ 13 # buffered-file: a pointer to the backing store, followed by a 'buffer' stream
+ 14 Stdin:  # buffered-file
+ 15     # file descriptor or (addr stream byte)
+ 16     0/imm32  # standard input
+ 17 $Stdin->buffer:
+ 18     # inlined fields for a stream
+ 19     #   current write index
+ 20     0/imm32
+ 21     #   current read index
+ 22     0/imm32
+ 23     #   size
+ 24     8/imm32
+ 25     #   data
+ 26     00 00 00 00 00 00 00 00  # 8 bytes
+ 27 
+ 28 # TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But
+ 29 # I don't want to type in 1024 bytes here.
+ 30 
+ 31 == code
+ 32 #   instruction                     effective address                                                   register    displacement    immediate
+ 33 # . op          subop               mod             rm32          base        index         scale       r32
+ 34 # . 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
+ 35 
+ 36 # Return next byte value in eax, with top 3 bytes cleared.
+ 37 # On reaching end of file, return 0xffffffff (Eof).
+ 38 read-byte-buffered:  # f: (addr buffered-file) -> byte-or-Eof/eax: byte
+ 39     # . prologue
+ 40     55/push-ebp
+ 41     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+ 42     # . save registers
+ 43     51/push-ecx
+ 44     56/push-esi
+ 45     # esi = f
+ 46     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
+ 47     # ecx = f->read
+ 48     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(esi+8) to ecx
+ 49     # if (f->read >= f->write) populate stream from file
+ 50     3b/compare                      1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # compare ecx with *(esi+4)
+ 51     7c/jump-if-<  $read-byte-buffered:from-stream/disp8
+ 52     # . clear-stream(stream = f+4)
+ 53     # . . push args
+ 54     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy esi+4 to eax
+ 55     50/push-eax
+ 56     # . . call
+ 57     e8/call  clear-stream/disp32
+ 58     # . . discard args
+ 59     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 60     # . f->read must now be 0; update its cache at ecx
+ 61     31/xor                          3/mod/direct    1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # clear ecx
+ 62     # . eax = read(f->fd, stream = f+4)
+ 63     # . . push args
+ 64     50/push-eax
+ 65     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
+ 66     # . . call
+ 67     e8/call  read/disp32
+ 68     # . . discard args
+ 69     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 70     # if (eax == 0) return 0xffffffff
+ 71     3d/compare-eax-and  0/imm32
+ 72     75/jump-if-!=  $read-byte-buffered:from-stream/disp8
+ 73     b8/copy-to-eax  0xffffffff/imm32/Eof
+ 74     eb/jump  $read-byte-buffered:end/disp8
+ 75 $read-byte-buffered:from-stream:
+ 76     # byte-or-Eof = f->data[f->read]
+ 77     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
+ 78     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
+ 79     # ++f->read
+ 80     ff          0/subop/increment   1/mod/*+disp8   6/rm32/esi    .           .             .           .           8/disp8         .                 # increment *(esi+8)
+ 81 $read-byte-buffered:end:
+ 82     # . restore registers
+ 83     5e/pop-to-esi
+ 84     59/pop-to-ecx
+ 85     # . epilogue
+ 86     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+ 87     5d/pop-to-ebp
+ 88     c3/return
+ 89 
+ 90 # - tests
+ 91 
+ 92 test-read-byte-buffered-single:
+ 93     # - check that read-byte-buffered returns first byte of 'file'
+ 94     # setup
+ 95     # . clear-stream(_test-stream)
+ 96     # . . push args
+ 97     68/push  _test-stream/imm32
+ 98     # . . call
+ 99     e8/call  clear-stream/disp32
+100     # . . discard args
+101     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+102     # . clear-stream(_test-buffered-file->buffer)
+103     # . . push args
+104     68/push  $_test-buffered-file->buffer/imm32
+105     # . . call
+106     e8/call  clear-stream/disp32
+107     # . . discard args
+108     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+109     # . write(_test-stream, "Ab")
+110     # . . push args
+111     68/push  "Ab"/imm32
+112     68/push  _test-stream/imm32
+113     # . . call
+114     e8/call  write/disp32
+115     # . . discard args
+116     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+117     # read-byte-buffered(_test-buffered-file)
+118     # . . push args
+119     68/push  _test-buffered-file/imm32
+120     # . . call
+121     e8/call  read-byte-buffered/disp32
+122     # . . discard args
+123     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+124     # check-ints-equal(eax, 'A', msg)
+125     # . . push args
+126     68/push  "F - test-read-byte-buffered-single"/imm32
+127     68/push  0x41/imm32
+128     50/push-eax
+129     # . . call
+130     e8/call  check-ints-equal/disp32
+131     # . . discard args
+132     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+133     # . end
+134     c3/return
+135 
+136 test-read-byte-buffered-multiple:
+137     # - call read-byte-buffered twice, check that second call returns second byte
+138     # setup
+139     # . clear-stream(_test-stream)
+140     # . . push args
+141     68/push  _test-stream/imm32
+142     # . . call
+143     e8/call  clear-stream/disp32
+144     # . . discard args
+145     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+146     # . clear-stream($_test-buffered-file->buffer)
+147     # . . push args
+148     68/push  $_test-buffered-file->buffer/imm32
+149     # . . call
+150     e8/call  clear-stream/disp32
+151     # . . discard args
+152     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+153     # . write(_test-stream, "Ab")
+154     # . . push args
+155     68/push  "Ab"/imm32
+156     68/push  _test-stream/imm32
+157     # . . call
+158     e8/call  write/disp32
+159     # . . discard args
+160     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+161     # read-byte-buffered(_test-buffered-file)
+162     # . . push args
+163     68/push  _test-buffered-file/imm32
+164     # . . call
+165     e8/call  read-byte-buffered/disp32
+166     # . . discard args
+167     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+168     # read-byte-buffered(_test-buffered-file)
+169     # . . push args
+170     68/push  _test-buffered-file/imm32
+171     # . . call
+172     e8/call  read-byte-buffered/disp32
+173     # . . discard args
+174     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+175     # check-ints-equal(eax, 'b', msg)
+176     # . . push args
+177     68/push  "F - test-read-byte-buffered-multiple"/imm32
+178     68/push  0x62/imm32
+179     50/push-eax
+180     # . . call
+181     e8/call  check-ints-equal/disp32
+182     # . . discard args
+183     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+184     # . end
+185     c3/return
+186 
+187 test-read-byte-buffered-end-of-file:
+188     # - call read-byte-buffered on an empty 'file', check that it returns Eof
+189     # setup
+190     # . clear-stream(_test-stream)
+191     # . . push args
+192     68/push  _test-stream/imm32
+193     # . . call
+194     e8/call  clear-stream/disp32
+195     # . . discard args
+196     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+197     # . clear-stream($_test-buffered-file->buffer)
+198     # . . push args
+199     68/push  $_test-buffered-file->buffer/imm32
+200     # . . call
+201     e8/call  clear-stream/disp32
+202     # . . discard args
+203     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+204     # read-byte-buffered(_test-buffered-file)
+205     # . . push args
+206     68/push  _test-buffered-file/imm32
+207     # . . call
+208     e8/call  read-byte-buffered/disp32
+209     # . . discard args
+210     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+211     # check-ints-equal(eax, 0xffffffff, msg)
+212     # . . push args
+213     68/push  "F - test-read-byte-buffered-end-of-file"/imm32
+214     68/push  0xffffffff/imm32/Eof
+215     50/push-eax
+216     # . . call
+217     e8/call  check-ints-equal/disp32
+218     # . . discard args
+219     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+220     # . end
+221     c3/return
+222 
+223 test-read-byte-buffered-refills-buffer:
+224     # - consume buffered-file's buffer, check that next read-byte-buffered still works
+225     # setup
+226     # . clear-stream(_test-stream)
+227     # . . push args
+228     68/push  _test-stream/imm32
+229     # . . call
+230     e8/call  clear-stream/disp32
+231     # . . discard args
+232     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+233     # . clear-stream($_test-buffered-file->buffer)
+234     # . . push args
+235     68/push  $_test-buffered-file->buffer/imm32
+236     # . . call
+237     e8/call  clear-stream/disp32
+238     # . . discard args
+239     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+240     # . write(_test-stream, "Abcdefgh")
+241     # . . push args
+242     68/push  "Abcdefgh"/imm32
+243     68/push  _test-stream/imm32
+244     # . . call
+245     e8/call  write/disp32
+246     # . . discard args
+247     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+248     # pretend buffer is full
+249     # . _test-buffered-file->read = 6  # >= _test-buffered-file->size
+250     b8/copy-to-eax  _test-buffered-file/imm32
+251     c7          0/subop/copy        1/mod/*+disp8   0/rm32/eax    .           .             .           .           8/disp8         6/imm32           # copy to *(eax+8)
+252     # read-byte-buffered(_test-buffered-file)
+253     # . . push args
+254     68/push  _test-buffered-file/imm32
+255     # . . call
+256     e8/call  read-byte-buffered/disp32
+257     # . . discard args
+258     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+259     # check-ints-equal(eax, 'A', msg)
+260     # . . push args
+261     68/push  "F - test-read-byte-buffered-refills-buffer"/imm32
+262     68/push  0x41/imm32
+263     50/push-eax
+264     # . . call
+265     e8/call  check-ints-equal/disp32
+266     # . . discard args
+267     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+268     # . end
+269     c3/return
+270 
+271 # Return next byte value in eax, with top 3 bytes cleared.
+272 # Abort on reaching end of stream.
+273 read-byte:  # s: (addr stream byte) -> result/eax: byte
+274     # . prologue
+275     55/push-ebp
+276     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+277     # . save registers
+278     51/push-ecx
+279     56/push-esi
+280     # esi = s
+281     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
+282     # ecx = s->read
+283     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
+284     # if (f->read >= f->write) abort
+285     3b/compare                      0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare ecx with *esi
+286     0f 8d/jump-if->=  $read-byte:abort/disp32
+287     # result = f->data[f->read]
+288     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
+289     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
+290     # ++f->read
+291     ff          0/subop/increment   1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # increment *(esi+4)
+292 $read-byte:end:
+293     # . restore registers
+294     5e/pop-to-esi
+295     59/pop-to-ecx
+296     # . epilogue
+297     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+298     5d/pop-to-ebp
+299     c3/return
+300 
+301 $read-byte:abort:
+302     # . _write(2/stderr, error)
+303     # . . push args
+304     68/push  "read-byte: empty stream\n"/imm32
+305     68/push  2/imm32/stderr
+306     # . . call
+307     e8/call  _write/disp32
+308     # . . discard args
+309     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+310     # . syscall(exit, 1)
+311     bb/copy-to-ebx  1/imm32
+312     e8/call  syscall_exit/disp32
+313     # never gets here
+314 
+315 == data
+316 
+317 # a test buffered file for _test-stream
+318 _test-buffered-file:  # buffered-file
+319     # file descriptor or (addr stream byte)
+320     _test-stream/imm32
+321 $_test-buffered-file->buffer:
+322     # current write index
+323     0/imm32
+324     # current read index
+325     0/imm32
+326     # size
+327     6/imm32
+328     # data
+329     00 00 00 00 00 00  # 6 bytes
+330 
+331 _test-input-stream:  # (stream byte)
+332     # current write index
+333     0/imm32
+334     # current read index
+335     0/imm32
+336     # size
+337     0x400/imm32  # 1024 bytes
+338     # data (64 lines x 16 bytes/line)
+339     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+340     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+341     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+342     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+343     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+344     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+345     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+346     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+347     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+348     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+349     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+350     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+351     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+352     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+353     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+354     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+355     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+356     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+357     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+358     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+359     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+360     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+361     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+362     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+363     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+364     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+365     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+366     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+367     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+368     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+369     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+370     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+371     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+372     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+373     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+374     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+375     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+376     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+377     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+378     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+379     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+380     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+381     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+382     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+383     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+384     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+385     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+386     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+387     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+388     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+389     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+390     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+391     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+392     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+393     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+394     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+395     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+396     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+397     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+398     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+399     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+400     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+401     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+402     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+403 
+404 # a test buffered file for _test-input-stream
+405 _test-input-buffered-file:  # buffered-file
+406     # file descriptor or (addr stream byte)
+407     _test-input-stream/imm32
+408 $_test-input-buffered-file->buffer:
+409     # current write index
+410     0/imm32
+411     # current read index
+412     0/imm32
+413     # size
+414     6/imm32
+415     # data
+416     00 00 00 00 00 00  # 6 bytes
+417 
+418 # . . vim:nowrap:textwidth=0
+
+ + + -- cgit 1.4.1-2-gfad0