https://github.com/akkartik/mu/blob/master/subx/064write-byte.subx

      1 # write-byte: write a single byte to a buffered-file. The write may be buffered.
      2 # flush: write out any buffered writes to disk.
      3 #
      4 # TODO: Come up with a way to signal failure to write to disk. This is hard
      5 # since the failure may impact previous calls that were buffered.
      6
      7 == data
      8
      9 # The buffered file for standard output.
     10 Stdout:
     11     # file descriptor or (address stream)
     12     1/imm32  # standard output
     13     # current write index
     14     0/imm32
     15     # current read index
     16     0/imm32
     17     # length
     18     8/imm32
     19     # data
     20     00 00 00 00 00 00 00 00  # 8 bytes
     21
     22 # TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But
     23 # I don't want to type in 1024 bytes here.
     24
     25 == code
     26 #   instruction                     effective address                                                   register    displacement    immediate
     27 # . op          subop               mod             rm32          base        index         scale       r32
     28 # . 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
     29
     30 # Write lower byte of 'n' to 'f'.
     31 write-byte:  # f : (address buffered-file), n : int -> <void>
     32     # . prolog
     33     55/push-EBP
     34     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     35     # . save registers
     36     51/push-ECX
     37     57/push-EDI
     38     # EDI = f
     39     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
     40     # ECX = f->write
     41     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
     42     # if (f->write >= f->length) flush and clear f's stream
     43     3b/compare                      1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   0xc/disp8       .                 # compare ECX with *(EDI+12)
     44     7c/jump-if-lesser  $write-byte:to-stream/disp8
     45     # . flush(f)
     46     # . . push args
     47     57/push-EDI
     48     # . . call
     49     e8/call  flush/disp32
     50     # . . discard args
     51     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     52     # . clear-stream(stream = f+4)
     53     # . . push args
     54     8d/copy-address                 1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EDI+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->write must now be 0; update its cache at ECX
     61     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
     62 $write-byte:to-stream:
     63     # write to stream
     64     # f->data[f->write] = LSB(n)
     65     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
     66     8a/copy-byte                    1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/AL    0xc/disp8       .                 # copy byte at *(EBP+12) to AL
     67     88/copy-byte                    1/mod/*+disp8   4/rm32/sib    7/base/EDI  1/index/ECX   .           0/r32/AL    0x10/disp8      .                 # copy AL to *(EDI+ECX+16)
     68     # ++f->write
     69     ff          0/subop/increment   1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         .                 # increment *(EDI+4)
     70 $write-byte:end:
     71     # . restore registers
     72     5f/pop-to-EDI
     73     59/pop-to-ECX
     74     # . epilog
     75     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     76     5d/pop-to-EBP
     77     c3/return
     78
     79 flush:  # f : (address buffered-file) -> <void>
     80     # . prolog
     81     55/push-EBP
     82     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     83     # . save registers
     84     50/push-EAX
     85     51/push-ECX
     86     # EAX = f
     87     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
     88     # write-stream(f->fd, data = f+4)
     89       # . . push args
     90     8d/copy-address                 1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy EAX+4 to ECX
     91     51/push-ECX
     92     ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
     93       # . . call
     94     e8/call  write-stream/disp32
     95       # . . discard args
     96     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     97 $flush:end:
     98     # . restore registers
     99     59/pop-to-ECX
    100     58/pop-to-EAX
    101     # . epilog
    102     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    103     5d/pop-to-EBP
    104     c3/return
    105
    106 # - tests
    107
    108 test-write-byte-single:
    109     # - check that write-byte writes to first byte of 'file'
    110     # setup
    111     # . clear-stream(_test-stream)
    112     # . . push args
    113     68/push  _test-stream/imm32
    114     # . . call
    115     e8/call  clear-stream/disp32
    116     # . . discard args
    117     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    118     # . clear-stream(_test-buffered-file+4)
    119     # . . push args
    120     b8/copy-to-EAX  _test-buffered-file/imm32
    121     05/add-to-EAX  4/imm32
    122     50/push-EAX
    123     # . . call
    124     e8/call  clear-stream/disp32
    125     # . . discard args
    126     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    127     # write-byte(_test-buffered-file, 'A')
    128     # . . push args
    129     68/push  0x41/imm32
    130     68/push  _test-buffered-file/imm32
    131     # . . call
    132     e8/call  write-byte/disp32
    133     # . . discard args
    134     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    135     # flush(_test-buffered-file)
    136     # . . push args
    137     68/push  _test-buffered-file/imm32
    138     # . . call
    139     e8/call  flush/disp32
    140     # . . discard args
    141     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    142     # check-stream-equal(_test-stream, "A", msg)
    143     # . . push args
    144     68/push  "F - test-write-byte-single"/imm32
    145     68/push  "A"/imm32
    146     68/push  _test-stream/imm32
    147     # . . call
    148     e8/call  check-stream-equal/disp32
    149     # . . discard args
    150     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    151     # . end
    152     c3/return
    153
    154 test-write-byte-multiple-flushes:
    155     # - check that write-byte correctly flushes buffered data
    156     # setup
    157     # . clear-stream(_test-stream)
    158     # . . push args
    159     68/push  _test-stream/imm32
    160     # . . call
    161     e8/call  clear-stream/disp32
    162     # . . discard args
    163     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    164     # . clear-stream(_test-buffered-file+4)
    165     # . . push args
    166     b8/copy-to-EAX  _test-buffered-file/imm32
    167     05/add-to-EAX  4/imm32
    168     50/push-EAX
    169     # . . call
    170     e8/call  clear-stream/disp32
    171     # . . discard args
    172     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    173     # fill up the buffer for _test-buffered-file
    174     # . write(_test-buffered-file+4, 'abcdef')
    175     # . . push args
    176     68/push  "abcdef"/imm32
    177     b8/copy-to-EAX  _test-buffered-file/imm32
    178     05/add-to-EAX  4/imm32
    179     50/push-EAX
    180     # . . call
    181     e8/call  write/disp32
    182     # . . discard args
    183     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    184     # write-byte(_test-buffered-file, 'g')
    185     # . . push args
    186     68/push  0x67/imm32
    187     68/push  _test-buffered-file/imm32
    188     # . . call
    189     e8/call  write-byte/disp32
    190     # . . discard args
    191     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    192     # flush(_test-buffered-file)
    193     # . . push args
    194     68/push  _test-buffered-file/imm32
    195     # . . call
    196     e8/call  flush/disp32
    197     # . . discard args
    198     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    199     # check-stream-equal(_test-stream, "abcdef", msg)
    200     # . . push args
    201     68/push  "F - test-write-byte-multiple-flushes: 1"/imm32
    202     68/push  "abcdefg"/imm32
    203     68/push  _test-stream/imm32
    204     # . . call
    205     e8/call  check-stream-equal/disp32
    206     # . . discard args
    207     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    208     # . end
    209     c3/return
    210
    211 # . . vim:nowrap:textwidth=0