about summary refs log tree commit diff stats
path: root/subx/062write-byte.subx
blob: ad29d9f19b418470b32da70c6aa08c25e2aff434 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# write-byte: write a single byte to a buffered-file. The write may be buffered.
# flush: write out any buffered writes to disk.
#
# TODO: Come up with a way to signal failure to write to disk. This is hard
# since the failure may impact previous calls that were buffered.

== data

# The buffered file for standard output.
Stdout:
    # file descriptor or (address stream)
    01 00 00 00  # 1 = standard output
    # current write index
    00 00 00 00
    # current read index
    00 00 00 00
    # length (8)
    08 00 00 00
    # 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.

== 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

# main:
    e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
    # syscall(exit, Num-test-failures)
    8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
    b8/copy-to-EAX  1/imm32/exit
    cd/syscall  0x80/imm8

# Write lower byte of 'n' to 'f'.
write-byte:  # f : (address buffered-file), n : int -> <void>
    # . prolog
    55/push-EBP
    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    # . save registers
    51/push-ECX
    57/push-EDI
    # EDI = f
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
    # ECX = f->write
    8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
    # if (f->write >= f->length) flush and clear f's stream
    3b/compare                      1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   0xc/disp8       .                 # compare ECX with *(EDI+12)
    7c/jump-if-lesser  $write-byte:to-stream/disp8
    # . 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
$write-byte:to-stream:
    # write to stream
    # f->data[f->read] = LSB(n)
    31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    8a/copy-byte                    1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/AL    0xc/disp8       .                 # copy byte at *(EBP+12) to AL
    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)
    # ++f->read
    ff          0/subop/increment   1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         .                 # increment *(EDI+4)
$write-byte:end:
    # . restore registers
    5f/pop-to-EDI
    59/pop-to-ECX
    # . epilog
    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5d/pop-to-EBP
    c3/return

flush:  # f : (address buffered-file) -> <void>
    # . 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
    # EAX = f
    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
    # write-stream(f->fd, data = f+4)
      # . . push args
    8d/copy-address                 1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy EAX+4 to ECX
    51/push-ECX
    ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
      # . . call
    e8/call  write-stream/disp32
      # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
$flush:end:
    # . restore registers
    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

# - tests

test-write-byte-single:
    # - check that read-byte returns first byte of '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-byte(_test-buffered-file, 'A')
    # . . push args
    68/push  0x41/imm32
    68/push  _test-buffered-file/imm32
    # . . call
    e8/call  write-byte/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-ints-equal(*_test-stream->data, 'A', msg)
    # . . push args
    68/push  "F - test-write-byte-single"/imm32
    68/push  0x41/imm32
    # . . push *_test-stream->data
    b8/copy-to-EAX  _test-stream/imm32
    ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
    # . . 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

# . . vim:nowrap:textwidth=0