move comment to a clearer location
[mu.git] / 105string-equal.subx
blob2289e5148abcf6bba6e4ba6d17d4cfacc1ab1eae
1 # Comparing 'regular' size-prefixed strings.
3 == code
4 #   instruction                     effective address                                                   register    displacement    immediate
5 # . op          subop               mod             rm32          base        index         scale       r32
6 # . 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
8 string-equal?:  # s: (addr array byte), benchmark: (addr array byte) -> result/eax: boolean
9     # pseudocode:
10     #   if (s->size != benchmark->size) return false
11     #   return string-starts-with?(s, benchmark)
12     #
13     # . prologue
14     55/push-ebp
15     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
16     # . save registers
17     51/push-ecx
18     56/push-esi
19     57/push-edi
20     # esi = s
21     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
22     # edi = benchmark
23     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
24     # ecx = s->size
25     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
26 $string-equal?:sizes:
27     # if (ecx != benchmark->size) return false
28     39/compare                      0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # compare *edi and ecx
29     b8/copy-to-eax  0/imm32/false
30     75/jump-if-!=  $string-equal?:end/disp8
31 $string-equal?:contents:
32     # string-starts-with?(s, benchmark)
33     # . . push args
34     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
35     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
36     # . . call
37     e8/call  string-starts-with?/disp32
38     # . . discard args
39     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
40 $string-equal?:end:
41     # . restore registers
42     5f/pop-to-edi
43     5e/pop-to-esi
44     59/pop-to-ecx
45     # . epilogue
46     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
47     5d/pop-to-ebp
48     c3/return
50 string-starts-with?:  # s: (addr array byte), benchmark: (addr array byte) -> result/eax: boolean
51     # pseudocode:
52     #   if (s->size < benchmark->size) return false
53     #   currs = s->data
54     #   currb = benchmark->data
55     #   maxb = &benchmark->data[benchmark->size]
56     #   while currb < maxb
57     #     c1 = *currs
58     #     c2 = *currb
59     #     if (c1 != c2) return false
60     #     ++currs, ++currb
61     #   return true
62     #
63     # registers:
64     #   currs: esi
65     #   maxs: ecx
66     #   currb: edi
67     #   c1: eax
68     #   c2: ebx
69     #
70     # . prologue
71     55/push-ebp
72     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
73     # . save registers
74     51/push-ecx
75     52/push-edx
76     56/push-esi
77     57/push-edi
78     # esi = s
79     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
80     # edi = benchmark
81     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
82     # var bsize/ecx: int = benchmark->size
83     8b/copy                         0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # copy *edi to ecx
84 $string-starts-with?:sizes:
85     # if (s->size < bsize) return false
86     39/compare                      0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare *esi with ecx
87     7c/jump-if-<  $string-starts-with?:false/disp8
88     # var currs/esi: (addr byte) = s->data
89     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               4/imm32           # add to esi
90     # var currb/edi: (addr byte) = benchmark->data
91     81          0/subop/add         3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm32           # add to edi
92     # var maxb/ecx: (addr byte) = &benchmark->data[benchmark->size]
93     01/add                          3/mod/direct    1/rm32/ecx    .           .             .           7/r32/edi   .               .                 # add edi to ecx
94     # var c1/eax: byte = 0
95     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
96     # var c2/edx: byte = 0
97     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
98 $string-starts-with?:loop:
99     # if (currs >= maxs) return true
100     39/compare                      3/mod/direct    7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # compare edi with ecx
101     73/jump-if-addr>=  $string-starts-with?:true/disp8
102     # c1 = *currs
103     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
104     # c2 = *currb
105     8a/copy-byte                    0/mod/indirect  7/rm32/edi    .           .             .           2/r32/DL    .               .                 # copy byte at *edi to DL
106     # if (c1 != c2) return false
107     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax and edx
108     75/jump-if-!=  $string-starts-with?:false/disp8
109     # ++currs
110     46/increment-esi
111     # ++currb
112     47/increment-edi
113     eb/jump  $string-starts-with?:loop/disp8
114 $string-starts-with?:true:
115     b8/copy-to-eax  1/imm32
116     eb/jump  $string-starts-with?:end/disp8
117 $string-starts-with?:false:
118     b8/copy-to-eax  0/imm32
119 $string-starts-with?:end:
120     # . restore registers
121     5f/pop-to-edi
122     5e/pop-to-esi
123     5a/pop-to-edx
124     59/pop-to-ecx
125     # . epilogue
126     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
127     5d/pop-to-ebp
128     c3/return
130 # - tests
132 test-compare-empty-with-empty-string:
133     # eax = string-equal?("", "")
134     # . . push args
135     68/push  ""/imm32
136     68/push  ""/imm32
137     # . . call
138     e8/call  string-equal?/disp32
139     # . . discard args
140     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
141     # check-ints-equal(eax, 1, msg)
142     # . . push args
143     68/push  "F - test-compare-empty-with-empty-string"/imm32
144     68/push  1/imm32/true
145     50/push-eax
146     # . . call
147     e8/call  check-ints-equal/disp32
148     # . . discard args
149     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
150     c3/return
152 test-compare-empty-with-non-empty-string:  # also checks size-mismatch code path
153     # eax = string-equal?("", "Abc")
154     # . . push args
155     68/push  "Abc"/imm32
156     68/push  ""/imm32
157     # . . call
158     e8/call  string-equal?/disp32
159     # . . discard args
160     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
161     # check-ints-equal(eax, 0, msg)
162     # . . push args
163     68/push  "F - test-compare-empty-with-non-empty-string"/imm32
164     68/push  0/imm32/false
165     50/push-eax
166     # . . call
167     e8/call  check-ints-equal/disp32
168     # . . discard args
169     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
170     c3/return
172 test-compare-equal-strings:
173     # eax = string-equal?("Abc", "Abc")
174     # . . push args
175     68/push  "Abc"/imm32
176     68/push  "Abc"/imm32
177     # . . call
178     e8/call  string-equal?/disp32
179     # . . discard args
180     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
181     # check-ints-equal(eax, 1, msg)
182     # . . push args
183     68/push  "F - test-compare-equal-strings"/imm32
184     68/push  1/imm32/true
185     50/push-eax
186     # . . call
187     e8/call  check-ints-equal/disp32
188     # . . discard args
189     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
190     c3/return
192 test-compare-inequal-strings-equal-sizes:
193     # eax = string-equal?("Abc", "Adc")
194     # . . push args
195     68/push  "Adc"/imm32
196     68/push  "Abc"/imm32
197     # . . call
198     e8/call  string-equal?/disp32
199     # . . discard args
200     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
201     # check-ints-equal(eax, 0, msg)
202     # . . push args
203     68/push  "F - test-compare-inequal-strings-equal-sizes"/imm32
204     68/push  0/imm32/false
205     50/push-eax
206     # . . call
207     e8/call  check-ints-equal/disp32
208     # . . discard args
209     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
210     c3/return
212 # helper for later tests
213 check-strings-equal:  # s: (addr array byte), expected: (addr array byte), msg: (addr array byte)
214     # . prologue
215     55/push-ebp
216     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
217     # . save registers
218     50/push-eax
219     # var eax: boolean = string-equal?(s, expected)
220     # . . push args
221     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
222     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
223     # . . call
224     e8/call  string-equal?/disp32
225     # . . discard args
226     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
227     # check-ints-equal(eax, 1, msg)
228     # . . push args
229     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
230     68/push  1/imm32
231     50/push-eax
232     # . . call
233     e8/call  check-ints-equal/disp32
234     # . . discard args
235     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
236 $check-strings-equal:end:
237     # . restore registers
238     58/pop-to-eax
239     # . epilogue
240     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
241     5d/pop-to-ebp
242     c3/return
244 # test the helper
245 test-check-strings-equal:
246     # check-strings-equal("Abc", "Abc")
247     # . . push args
248     68/push  "Abc"/imm32
249     68/push  "Abc"/imm32
250     # . . call
251     e8/call  check-strings-equal/disp32
252     # . . discard args
253     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
254     c3/return
256 # . . vim:nowrap:textwidth=0